diff --git a/.buildkite/ftr_base_serverless_configs.yml b/.buildkite/ftr_base_serverless_configs.yml index 5e7baeb3c3aea..2ff9ba6678462 100644 --- a/.buildkite/ftr_base_serverless_configs.yml +++ b/.buildkite/ftr_base_serverless_configs.yml @@ -1,6 +1,8 @@ disabled: # Base config files, only necessary to inform config finding script + # Serverless deployment-agnostic default config for api-integration tests + - x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts # Serverless base config files - x-pack/test_serverless/api_integration/config.base.ts - x-pack/test_serverless/functional/config.base.ts diff --git a/.buildkite/ftr_oblt_serverless_configs.yml b/.buildkite/ftr_oblt_serverless_configs.yml index 085c25f2d80a6..8fe505ff0e93e 100644 --- a/.buildkite/ftr_oblt_serverless_configs.yml +++ b/.buildkite/ftr_oblt_serverless_configs.yml @@ -26,3 +26,5 @@ enabled: - x-pack/test_serverless/functional/test_suites/observability/common_configs/config.group5.ts - x-pack/test_serverless/functional/test_suites/observability/common_configs/config.group6.ts - x-pack/test_serverless/functional/test_suites/observability/config.screenshots.ts + # serverless config files that run deployment-agnostic tests + - x-pack/test/api_integration/deployment_agnostic/oblt.serverless.config.ts diff --git a/.buildkite/ftr_platform_stateful_configs.yml b/.buildkite/ftr_platform_stateful_configs.yml index 71ba8932271e8..a984afc263170 100644 --- a/.buildkite/ftr_platform_stateful_configs.yml +++ b/.buildkite/ftr_platform_stateful_configs.yml @@ -1,4 +1,6 @@ disabled: + # Stateful base config for deployment-agnostic tests + - x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts # Base config files, only necessary to inform config finding script - test/functional/config.base.js - test/functional/firefox/config.base.ts @@ -155,7 +157,6 @@ enabled: - x-pack/test/api_integration/apis/monitoring/config.ts - x-pack/test/api_integration/apis/monitoring_collection/config.ts - x-pack/test/api_integration/apis/osquery/config.ts - - x-pack/test/api_integration/apis/painless_lab/config.ts - x-pack/test/api_integration/apis/search/config.ts - x-pack/test/api_integration/apis/searchprofiler/config.ts - x-pack/test/api_integration/apis/security/config.ts @@ -359,3 +360,5 @@ enabled: - x-pack/performance/journeys_e2e/apm_service_inventory.ts - x-pack/performance/journeys_e2e/infra_hosts_view.ts - x-pack/test/custom_branding/config.ts + # stateful config files that run deployment-agnostic tests + - x-pack/test/api_integration/deployment_agnostic/stateful.config.ts diff --git a/.buildkite/ftr_search_serverless_configs.yml b/.buildkite/ftr_search_serverless_configs.yml index 73b6238027bce..9a5ce6798dbae 100644 --- a/.buildkite/ftr_search_serverless_configs.yml +++ b/.buildkite/ftr_search_serverless_configs.yml @@ -16,3 +16,5 @@ enabled: - x-pack/test_serverless/functional/test_suites/search/common_configs/config.group4.ts - x-pack/test_serverless/functional/test_suites/search/common_configs/config.group5.ts - x-pack/test_serverless/functional/test_suites/search/common_configs/config.group6.ts + # serverless config files that run deployment-agnostic tests + - x-pack/test/api_integration/deployment_agnostic/search.serverless.config.ts diff --git a/.buildkite/ftr_security_serverless_configs.yml b/.buildkite/ftr_security_serverless_configs.yml index 3880175623fdd..4c3b037ce9f8a 100644 --- a/.buildkite/ftr_security_serverless_configs.yml +++ b/.buildkite/ftr_security_serverless_configs.yml @@ -97,3 +97,5 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/edr_workflows/response_actions/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_endpoint/configs/serverless.endpoint.config.ts - x-pack/test/security_solution_endpoint/configs/serverless.integrations.config.ts + # serverless config files that run deployment-agnostic tests + - x-pack/test/api_integration/deployment_agnostic/security.serverless.config.ts diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index 8fb66cea9ee3d..148d78583a613 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -83,4 +83,5 @@ enabled: - x-pack/test/api_integration/apis/cloud_security_posture/config.ts - x-pack/test/cloud_security_posture_api/config.ts - x-pack/test/cloud_security_posture_functional/config.ts + - x-pack/test/cloud_security_posture_functional/config.agentless.ts - x-pack/test/cloud_security_posture_functional/data_views/config.ts diff --git a/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml b/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml index 6679e3006bb00..5a89b3bfb4f3a 100644 --- a/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml +++ b/.buildkite/pipeline-resource-definitions/kibana-fips-daily.yml @@ -19,12 +19,12 @@ spec: description: Run Kibana FIPS smoke tests spec: env: - SLACK_NOTIFICATIONS_CHANNEL: "#kibana-fips-ftr-errors" - ELASTIC_SLACK_NOTIFICATIONS_ENABLED: "true" + SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' + ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' repository: elastic/kibana branch_configuration: main default_branch: main - pipeline_file: ".buildkite/pipelines/fips.yml" + pipeline_file: '.buildkite/pipelines/fips.yml' provider_settings: trigger_mode: none schedules: diff --git a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml index aae27bd38af0f..cb0b63852ad00 100644 --- a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml +++ b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml @@ -44,7 +44,7 @@ steps: timeout_in_minutes: 60 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 3 - label: "Pick Test Group Run Order (FTR + Integration)" @@ -52,18 +52,18 @@ steps: depends_on: build timeout_in_minutes: 10 env: - FTR_CONFIGS_SCRIPT: 'TEST_ES_SERVERLESS_IMAGE=$ES_SERVERLESS_IMAGE .buildkite/scripts/steps/test/ftr_configs.sh' - JEST_INTEGRATION_SCRIPT: 'TEST_ES_SERVERLESS_IMAGE=$ES_SERVERLESS_IMAGE .buildkite/scripts/steps/test/jest_integration.sh' - FTR_CONFIG_PATTERNS: '**/test_serverless/**' - FTR_EXTRA_ARGS: '$FTR_EXTRA_ARGS' - LIMIT_CONFIG_TYPE: 'functional,integration' + FTR_CONFIGS_SCRIPT: "TEST_ES_SERVERLESS_IMAGE=$ES_SERVERLESS_IMAGE .buildkite/scripts/steps/test/ftr_configs.sh" + JEST_INTEGRATION_SCRIPT: "TEST_ES_SERVERLESS_IMAGE=$ES_SERVERLESS_IMAGE .buildkite/scripts/steps/test/jest_integration.sh" + FTR_CONFIG_PATTERNS: "**/test_serverless/**,**/test/security_solution_api_integration/**/serverless.config.ts" + FTR_EXTRA_ARGS: "$FTR_EXTRA_ARGS" + LIMIT_CONFIG_TYPE: "functional,integration" retry: automatic: - - exit_status: '*' + - exit_status: "*" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_entity_analytics.sh - label: 'Serverless Entity Analytics - Security Solution Cypress Tests' + label: "Serverless Entity Analytics - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -76,11 +76,11 @@ steps: parallelism: 3 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_explore.sh - label: 'Serverless Explore - Security Solution Cypress Tests' + label: "Serverless Explore - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -93,11 +93,11 @@ steps: parallelism: 4 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_investigations.sh - label: 'Serverless Investigations - Security Solution Cypress Tests' + label: "Serverless Investigations - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -110,11 +110,11 @@ steps: parallelism: 8 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_rule_management.sh - label: 'Serverless Rule Management - Security Solution Cypress Tests' + label: "Serverless Rule Management - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -127,11 +127,11 @@ steps: parallelism: 5 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_rule_management_prebuilt_rules.sh - label: 'Serverless Rule Management - Prebuilt Rules - Security Solution Cypress Tests' + label: "Serverless Rule Management - Prebuilt Rules - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -144,11 +144,11 @@ steps: parallelism: 1 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_detection_engine.sh - label: 'Serverless Detection Engine - Security Solution Cypress Tests' + label: "Serverless Detection Engine - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -161,11 +161,11 @@ steps: parallelism: 5 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_detection_engine_exceptions.sh - label: 'Serverless Detection Engine - Exceptions - Security Solution Cypress Tests' + label: "Serverless Detection Engine - Exceptions - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -178,11 +178,11 @@ steps: parallelism: 2 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_ai_assistant.sh - label: 'Serverless AI Assistant - Security Solution Cypress Tests' + label: "Serverless AI Assistant - Security Solution Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -195,11 +195,11 @@ steps: parallelism: 1 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh - label: 'Defend Workflows Cypress Tests on Serverless' + label: "Defend Workflows Cypress Tests on Serverless" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -214,11 +214,11 @@ steps: parallelism: 12 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - command: .buildkite/scripts/steps/functional/security_serverless_osquery.sh - label: 'Serverless Osquery Cypress Tests' + label: "Serverless Osquery Cypress Tests" if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: image: family/kibana-ubuntu-2004 @@ -231,7 +231,7 @@ steps: parallelism: 7 retry: automatic: - - exit_status: '-1' + - exit_status: "-1" limit: 1 - wait: ~ @@ -241,6 +241,6 @@ steps: - wait: ~ - - label: 'Post-Build' + - label: "Post-Build" command: .buildkite/scripts/lifecycle/post_build.sh timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/sonarqube.yml b/.buildkite/pipelines/sonarqube.yml index 61e275011140b..20f58df398ddf 100644 --- a/.buildkite/pipelines/sonarqube.yml +++ b/.buildkite/pipelines/sonarqube.yml @@ -2,8 +2,8 @@ env: SKIP_NODE_SETUP: true steps: - - label: ":sonarqube: Continuous Code Inspection" + - label: ':sonarqube: Continuous Code Inspection' agents: image: docker.elastic.co/cloud-ci/sonarqube/buildkite-scanner:latest - memory: 16G + memory: 32G command: /scan-source-code.sh diff --git a/.buildkite/scripts/common/env.sh b/.buildkite/scripts/common/env.sh index 1d401bab4282a..34b71b7004725 100755 --- a/.buildkite/scripts/common/env.sh +++ b/.buildkite/scripts/common/env.sh @@ -23,12 +23,14 @@ if [[ -d /opt/local-ssd/buildkite ]]; then mkdir -p "$TMPDIR" fi -KIBANA_PKG_BRANCH="$(jq -r .branch "$KIBANA_DIR/package.json")" -export KIBANA_PKG_BRANCH -export KIBANA_BASE_BRANCH="$KIBANA_PKG_BRANCH" +if command -v jq >/dev/null 2>&1; then + KIBANA_PKG_BRANCH="$(jq -r .branch "$KIBANA_DIR/package.json")" + export KIBANA_PKG_BRANCH + export KIBANA_BASE_BRANCH="$KIBANA_PKG_BRANCH" -KIBANA_PKG_VERSION="$(jq -r .version "$KIBANA_DIR/package.json")" -export KIBANA_PKG_VERSION + KIBANA_PKG_VERSION="$(jq -r .version "$KIBANA_DIR/package.json")" + export KIBANA_PKG_VERSION +fi # Detects and exports the final target branch when using a merge queue if [[ "${BUILDKITE_BRANCH:-}" == "gh-readonly-queue"* ]]; then diff --git a/.eslintrc.js b/.eslintrc.js index 853b1549d2b93..2b8c6c819bb3e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -618,7 +618,7 @@ module.exports = { 'test/*/*.config.ts', 'test/*/{tests,test_suites,apis,apps}/**/*', 'test/server_integration/**/*.ts', - 'x-pack/test/*/{tests,test_suites,apis,apps}/**/*', + 'x-pack/test/*/{tests,test_suites,apis,apps,deployment_agnostic}/**/*', 'x-pack/test/*/*config.*ts', 'x-pack/test/saved_object_api_integration/*/apis/**/*', 'x-pack/test/ui_capabilities/*/tests/**/*', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8c044f50bfc7d..1ee8936a725c0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -50,7 +50,6 @@ packages/kbn-apm-types @elastic/obs-ux-infra_services-team packages/kbn-apm-utils @elastic/obs-ux-infra_services-team test/plugin_functional/plugins/app_link_test @elastic/kibana-core x-pack/test/usage_collection/plugins/application_usage_test @elastic/kibana-core -x-pack/plugins/observability_solution/assets_data_access @elastic/obs-knowledge-team x-pack/test/security_api_integration/plugins/audit_log @elastic/kibana-security packages/kbn-avc-banner @elastic/security-defend-workflows packages/kbn-axe-config @elastic/kibana-qa @@ -392,6 +391,7 @@ src/plugins/embeddable @elastic/kibana-presentation x-pack/examples/embedded_lens_example @elastic/kibana-visualizations x-pack/plugins/encrypted_saved_objects @elastic/kibana-security x-pack/plugins/enterprise_search @elastic/search-kibana +x-pack/plugins/observability_solution/entities_data_access @elastic/obs-entities x-pack/packages/kbn-entities-schema @elastic/obs-entities x-pack/plugins/observability_solution/entity_manager @elastic/obs-entities examples/error_boundary @elastic/appex-sharedux @@ -1274,6 +1274,7 @@ x-pack/test/observability_ai_assistant_functional @elastic/obs-ai-assistant /x-pack/test_serverless/functional/test_suites/security/ftr/ @elastic/appex-qa /x-pack/test_serverless/functional/test_suites/common/home_page/ @elastic/appex-qa /x-pack/test_serverless/**/services/ @elastic/appex-qa +/packages/kbn-es/src/stateful_resources/roles.yml @elastic/appex-qa # Core /config/ @elastic/kibana-core diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 67f6410d71553..884120ec4407d 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-08-06 +date: 2024-08-09 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 ae292e813b5f3..50488fc76b2c0 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index 224a89a805305..7b3917be58317 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index f4b3845a6431d..e6bff13778b2a 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-08-06 +date: 2024-08-09 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 f6247786ad87e..3b4c372f8ea63 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -4128,7 +4128,15 @@ "section": "def-common.DataViewLazy", "text": "DataViewLazy" }, - ">; createDataViewLazy: (spec: ", + ">; getDataViewLazyFromCache: (id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>; createDataViewLazy: (spec: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 70bd9b8d5dba1..e153f8b1a4afd 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index d86e5448e3a5d..6707c3d01cb69 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/entities/services\" | \"GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries\" | \"GET /internal/apm/entities/services/{serviceName}/logs_error_rate_timeseries\" | \"POST /internal/apm/entities/services/detailed_statistics\" | \"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/transactions\" | \"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/has_entities\" | \"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_downstream_dependencies\" | \"GET /internal/apm/services/{serviceName}/profiling/flamegraph\" | \"GET /internal/apm/profiling/status\" | \"GET /internal/apm/services/{serviceName}/profiling/functions\" | \"GET /internal/apm/services/{serviceName}/profiling/hosts/flamegraph\" | \"GET /internal/apm/services/{serviceName}/profiling/hosts/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/entities/services\" | \"GET /internal/apm/entities/services/{serviceName}/logs_rate_timeseries\" | \"GET /internal/apm/entities/services/{serviceName}/logs_error_rate_timeseries\" | \"POST /internal/apm/entities/services/detailed_statistics\" | \"GET /internal/apm/entities/services/{serviceName}/summary\" | \"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/transactions\" | \"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/has_entities\" | \"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_downstream_dependencies\" | \"GET /internal/apm/services/{serviceName}/profiling/flamegraph\" | \"GET /internal/apm/profiling/status\" | \"GET /internal/apm/services/{serviceName}/profiling/functions\" | \"GET /internal/apm/services/{serviceName}/profiling/hosts/flamegraph\" | \"GET /internal/apm/services/{serviceName}/profiling/hosts/functions\" | \"POST /internal/apm/custom-dashboard\" | \"DELETE /internal/apm/custom-dashboard\" | \"GET /internal/apm/services/{serviceName}/dashboards\"" ], "path": "x-pack/plugins/observability_solution/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -6101,9 +6101,13 @@ "SavedServiceGroup", "[]; }>; } & ", "APMRouteCreateOptions", - "; \"POST /internal/apm/entities/services/detailed_statistics\": { endpoint: \"POST /internal/apm/entities/services/detailed_statistics\"; params?: ", + "; \"GET /internal/apm/entities/services/{serviceName}/summary\": { endpoint: \"GET /internal/apm/entities/services/{serviceName}/summary\"; params?: ", "TypeC", - "<{ query: ", + "<{ path: ", + "TypeC", + "<{ serviceName: ", + "StringC", + "; }>; query: ", "IntersectionC", "<[", "TypeC", @@ -6127,105 +6131,61 @@ }, ">]>; }>, ", "TypeC", - "<{ kuery: ", - "StringC", - "; }>, ", - "TypeC", "<{ start: ", "Type", "; end: ", "Type", - "; }>, ", - "IntersectionC", - "<[", - "PartialC", - "<{ offset: ", - "StringC", - "; }>, ", - "TypeC", - "<{ probability: ", - "Type", - "; }>, ", - "TypeC", - "<{ documentType: ", - "UnionC", - "<[", - "LiteralC", - "<", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.ApmDocumentType", - "text": "ApmDocumentType" - }, - ".ServiceTransactionMetric>, ", - "LiteralC", - "<", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.ApmDocumentType", - "text": "ApmDocumentType" - }, - ".TransactionMetric>, ", - "LiteralC", - "<", + "; }>]>; }> | undefined; handler: ({}: ", + "APMRouteHandlerResources", + " & { params: { path: { serviceName: string; }; query: { environment: \"ENVIRONMENT_NOT_DEFINED\" | \"ENVIRONMENT_ALL\" | ", + "Branded", + "]>; rollupInterval: ", + ">; } & { start: number; end: number; }; }; }) => Promise<", + "ServiceEntities", + ">; } & ", + "APMRouteCreateOptions", + "; \"POST /internal/apm/entities/services/detailed_statistics\": { endpoint: \"POST /internal/apm/entities/services/detailed_statistics\"; params?: ", + "TypeC", + "<{ query: ", + "IntersectionC", + "<[", + "TypeC", + "<{ environment: ", "UnionC", "<[", "LiteralC", - "<", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.RollupInterval", - "text": "RollupInterval" - }, - ".OneMinute>, ", - "LiteralC", - "<", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.RollupInterval", - "text": "RollupInterval" - }, - ".TenMinutes>, ", - "LiteralC", - "<", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.RollupInterval", - "text": "RollupInterval" - }, - ".SixtyMinutes>, ", + "<\"ENVIRONMENT_NOT_DEFINED\">, ", "LiteralC", + "<\"ENVIRONMENT_ALL\">, ", + "BrandC", "<", + "StringC", + ", ", { - "pluginId": "apmDataAccess", + "pluginId": "@kbn/io-ts-utils", "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.RollupInterval", - "text": "RollupInterval" + "docId": "kibKbnIoTsUtilsPluginApi", + "section": "def-common.NonEmptyStringBrand", + "text": "NonEmptyStringBrand" }, - ".None>]>; }>]>, ", + ">]>; }>, ", "TypeC", - "<{ bucketSizeInSeconds: ", + "<{ kuery: ", + "StringC", + "; }>, ", + "TypeC", + "<{ start: ", "Type", - "; }>]>; body: ", + "; end: ", + "Type", + "; }>]>; body: ", "TypeC", "<{ serviceNames: ", "Type", @@ -6241,41 +6201,7 @@ "section": "def-common.NonEmptyStringBrand", "text": "NonEmptyStringBrand" }, - ">; } & { kuery: string; } & { start: number; end: number; } & { offset?: string | undefined; } & { probability: number; } & { documentType: ", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.ApmDocumentType", - "text": "ApmDocumentType" - }, - ".TransactionMetric | ", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.ApmDocumentType", - "text": "ApmDocumentType" - }, - ".ServiceTransactionMetric | ", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.ApmDocumentType", - "text": "ApmDocumentType" - }, - ".TransactionEvent; rollupInterval: ", - { - "pluginId": "apmDataAccess", - "scope": "common", - "docId": "kibApmDataAccessPluginApi", - "section": "def-common.RollupInterval", - "text": "RollupInterval" - }, - "; } & { bucketSizeInSeconds: number; }; body: { serviceNames: string[]; }; }; }) => Promise<{ currentPeriod: { apm: { [x: string]: ", - "ServiceTransactionDetailedStat", - "; }; logErrorRate: { [x: string]: { x: number; y: number | null; }[]; }; logRate: { [x: string]: { x: number; y: number | null; }[]; }; }; }>; } & ", + ">; } & { kuery: string; } & { start: number; end: number; }; body: { serviceNames: string[]; }; }; }) => Promise<{ currentPeriod: { [x: string]: { serviceName: string; latency: { x: number; y: number | null; }[]; logErrorRate: { x: number; y: number | null; }[]; logRate: { x: number; y: number | null; }[]; throughput: { x: number; y: number | null; }[]; failedTransactionRate: { x: number; y: number | null; }[]; }; }; }>; } & ", "APMRouteCreateOptions", "; \"GET /internal/apm/entities/services/{serviceName}/logs_error_rate_timeseries\": { endpoint: \"GET /internal/apm/entities/services/{serviceName}/logs_error_rate_timeseries\"; params?: ", "TypeC", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index d796e4b6ae404..eaba1415e703b 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.devdocs.json b/api_docs/apm_data_access.devdocs.json index ee59228e5d335..7334b593ed85f 100644 --- a/api_docs/apm_data_access.devdocs.json +++ b/api_docs/apm_data_access.devdocs.json @@ -1922,7 +1922,9 @@ "section": "def-common.ApmDataSource", "text": "ApmDataSource" }, - " & { hasDocs: boolean; hasDurationSummaryField: boolean; })[]>; }" + " & { hasDocs: boolean; hasDurationSummaryField: boolean; })[]>; getHostNames: ({ start, end, size, query, documentSources }: ", + "HostNamesRequest", + ") => Promise; }" ], "path": "x-pack/plugins/observability_solution/apm_data_access/server/types.ts", "deprecated": false, @@ -2099,7 +2101,9 @@ "section": "def-common.ApmDataSource", "text": "ApmDataSource" }, - " & { hasDocs: boolean; hasDurationSummaryField: boolean; })[]>; }" + " & { hasDocs: boolean; hasDurationSummaryField: boolean; })[]>; getHostNames: ({ start, end, size, query, documentSources }: ", + "HostNamesRequest", + ") => Promise; }" ], "path": "x-pack/plugins/observability_solution/apm_data_access/server/types.ts", "deprecated": false, diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 7f80cab9338fb..c31515e6ab309 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.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 | |-------------------|-----------|------------------------|-----------------| -| 74 | 0 | 74 | 0 | +| 74 | 0 | 74 | 1 | ## Server diff --git a/api_docs/assets_data_access.mdx b/api_docs/assets_data_access.mdx index b2a54b48ffcb7..a189576951681 100644 --- a/api_docs/assets_data_access.mdx +++ b/api_docs/assets_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetsDataAccess title: "assetsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the assetsDataAccess plugin -date: 2024-08-06 +date: 2024-08-08 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetsDataAccess'] --- import assetsDataAccessObj from './assets_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 6c3f6b0d39c5e..7137b402d29c7 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 2a59a276a862a..eebf435c20f31 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 52d5816d55d51..087d4b69a0f11 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-08-06 +date: 2024-08-09 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 9366ba029a15a..76e44f78a1610 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index d7d2df1f11d99..7ef8e8d70a83e 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-08-06 +date: 2024-08-09 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 74be306612541..2d1e9611366ef 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-08-06 +date: 2024-08-09 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 4d8d1ed66c30b..18cf116a6811a 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-08-06 +date: 2024-08-09 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 4a2f48dc96d84..4299278dfb42e 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-08-06 +date: 2024-08-09 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 6a70b3db7fc0b..b5e1179b11315 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2024-08-06 +date: 2024-08-09 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 4f10cd64ba6fd..7a4b66e351b76 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.devdocs.json b/api_docs/console.devdocs.json index 8dc7eae8bb8b1..8201b8d4ca4ac 100644 --- a/api_docs/console.devdocs.json +++ b/api_docs/console.devdocs.json @@ -761,6 +761,24 @@ ], "returnComment": [] }, + { + "parentPluginId": "console", + "id": "def-public.ConsolePluginStart.openEmbeddedConsoleAlternateView", + "type": "Function", + "tags": [], + "label": "openEmbeddedConsoleAlternateView", + "description": [ + "\nopenEmbeddedConsoleAlternateView is available if the embedded console can be rendered.\nCalling this function will open the embedded console to the alternative view. If there is no alternative view registered\nthis will open the embedded console." + ], + "signature": [ + "(() => void) | undefined" + ], + "path": "src/plugins/console/public/types/plugin_dependencies.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "console", "id": "def-public.ConsolePluginStart.EmbeddableConsole", diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 4f96b389418fe..55777ee86ec7b 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 38 | 0 | 30 | 0 | +| 39 | 0 | 30 | 0 | ## Client diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index f347874b0dbfb..7ab2cf169aed4 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-08-06 +date: 2024-08-09 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 f889783f043f6..d7a2d16003262 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-08-06 +date: 2024-08-09 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 d572b073f073d..ae363d30cfae3 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-08-06 +date: 2024-08-09 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 604935aa7cf95..7ee85d1689799 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 17b0f551a4d5a..2e3a6f1ee629c 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-08-06 +date: 2024-08-09 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 1c7f9f96cb644..5af00566bd450 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -14378,6 +14378,46 @@ ], "returnComment": [] }, + { + "parentPluginId": "data", + "id": "def-server.DataViewsService.getDataViewLazyFromCache", + "type": "Function", + "tags": [], + "label": "getDataViewLazyFromCache", + "description": [], + "signature": [ + "(id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-server.DataViewsService.getDataViewLazyFromCache.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "data", "id": "def-server.DataViewsService.get", @@ -21787,6 +21827,46 @@ ], "returnComment": [] }, + { + "parentPluginId": "data", + "id": "def-common.DataViewsService.getDataViewLazyFromCache", + "type": "Function", + "tags": [], + "label": "getDataViewLazyFromCache", + "description": [], + "signature": [ + "(id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "data", + "id": "def-common.DataViewsService.getDataViewLazyFromCache.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "data", "id": "def-common.DataViewsService.get", @@ -25293,7 +25373,15 @@ "section": "def-common.DataViewLazy", "text": "DataViewLazy" }, - ">; createDataViewLazy: (spec: ", + ">; getDataViewLazyFromCache: (id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>; createDataViewLazy: (spec: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 952b288f76c75..8163cbe639019 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-08-06 +date: 2024-08-09 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 | |-------------------|-----------|------------------------|-----------------| -| 3205 | 31 | 2590 | 24 | +| 3209 | 31 | 2594 | 24 | ## Client diff --git a/api_docs/data_quality.mdx b/api_docs/data_quality.mdx index fcc29858d6256..78275dd7a5b39 100644 --- a/api_docs/data_quality.mdx +++ b/api_docs/data_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataQuality title: "dataQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the dataQuality plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataQuality'] --- import dataQualityObj from './data_quality.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 9add25efc42bb..107edac3a9ca7 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-08-06 +date: 2024-08-09 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 | |-------------------|-----------|------------------------|-----------------| -| 3205 | 31 | 2590 | 24 | +| 3209 | 31 | 2594 | 24 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index 6482a2c5ef6cf..0d2eb09086dc9 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -29664,7 +29664,15 @@ "section": "def-common.DataViewLazy", "text": "DataViewLazy" }, - ">; createDataViewLazy: (spec: ", + ">; getDataViewLazyFromCache: (id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>; createDataViewLazy: (spec: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index ef3ffbce20a49..f1edf2042a1b7 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-08-06 +date: 2024-08-09 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 | |-------------------|-----------|------------------------|-----------------| -| 3205 | 31 | 2590 | 24 | +| 3209 | 31 | 2594 | 24 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index bc91d5a106518..4553e1d8a5305 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.devdocs.json b/api_docs/data_view_field_editor.devdocs.json index 68e920109e0b6..05646d029a3dc 100644 --- a/api_docs/data_view_field_editor.devdocs.json +++ b/api_docs/data_view_field_editor.devdocs.json @@ -558,6 +558,14 @@ "section": "def-common.DataView", "text": "DataView" }, + " | ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, "; }" ], "path": "src/plugins/data_view_field_editor/public/open_delete_modal.tsx", @@ -1077,8 +1085,9 @@ "section": "def-public.OpenFieldDeleteModalOptions", "text": "OpenFieldDeleteModalOptions" }, - ") => ", - "CloseEditor" + ") => Promise<", + "CloseEditor", + ">" ], "path": "src/plugins/data_view_field_editor/public/types.ts", "deprecated": false, diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index e3e6ccf67fe18..5cd39996587e4 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-08-06 +date: 2024-08-09 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 3a83d13237aba..8ff977ac59a64 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-08-06 +date: 2024-08-09 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 fc8d38599f229..5bce761617486 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -1895,6 +1895,858 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy", + "type": "Class", + "tags": [], + "label": "DataViewLazy", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " extends ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.AbstractDataView", + "text": "AbstractDataView" + } + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.scriptedFieldsEnabled", + "type": "boolean", + "tags": [], + "label": "scriptedFieldsEnabled", + "description": [ + "\nReturns true if scripted fields are enabled" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "config", + "description": [], + "signature": [ + "DataViewDeps" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getFields", + "type": "Function", + "tags": [], + "label": "getFields", + "description": [], + "signature": [ + "({ mapped, scripted, runtime, fieldName, forceRefresh, unmapped, indexFilter, metaFields, }: GetFieldsParams) => Promise<{ getFieldMap: () => { [x: string]: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + }, + "; }; getFieldMapSorted: () => Record; }>" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getFields.$1", + "type": "Object", + "tags": [], + "label": "{\n mapped = true,\n scripted = true,\n runtime = true,\n fieldName,\n forceRefresh = false,\n unmapped,\n indexFilter,\n metaFields = true,\n }", + "description": [], + "signature": [ + "GetFieldsParams" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getRuntimeFields", + "type": "Function", + "tags": [], + "label": "getRuntimeFields", + "description": [], + "signature": [ + "({ fieldName }: Pick) => DataViewFieldMap" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getRuntimeFields.$1", + "type": "Object", + "tags": [], + "label": "{ fieldName = ['*'] }", + "description": [], + "signature": [ + "Pick" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.addRuntimeField", + "type": "Function", + "tags": [], + "label": "addRuntimeField", + "description": [ + "\nAdd a runtime field - Appended to existing mapped field or a new field is\ncreated as appropriate." + ], + "signature": [ + "(name: string, runtimeField: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.RuntimeField", + "text": "RuntimeField" + }, + ") => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + }, + "[]>" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.addRuntimeField.$1", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "Field name" + ], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.addRuntimeField.$2", + "type": "Object", + "tags": [], + "label": "runtimeField", + "description": [ + "Runtime field definition" + ], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.RuntimeField", + "text": "RuntimeField" + } + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.removeRuntimeField", + "type": "Function", + "tags": [], + "label": "removeRuntimeField", + "description": [ + "\nRemove a runtime field - removed from mapped field or removed unmapped\nfield as appropriate. Doesn't clear associated field attributes." + ], + "signature": [ + "(name: string) => void" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.removeRuntimeField.$1", + "type": "string", + "tags": [], + "label": "name", + "description": [ + "- Field name to remove" + ], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getScriptedFields", + "type": "Function", + "tags": [], + "label": "getScriptedFields", + "description": [], + "signature": [ + "({ fieldName }: Pick) => Record" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getScriptedFields.$1", + "type": "Object", + "tags": [], + "label": "{ fieldName = ['*'] }", + "description": [], + "signature": [ + "Pick" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getScriptedFieldsForQuery", + "type": "Function", + "tags": [], + "label": "getScriptedFieldsForQuery", + "description": [], + "signature": [ + "() => Record" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getComputedFields", + "type": "Function", + "tags": [], + "label": "getComputedFields", + "description": [], + "signature": [ + "({ fieldName }: { fieldName: string[]; }) => Promise<{ scriptFields: Record; docvalueFields: { field: string; format: string; }[]; runtimeFields: ", + "MappingRuntimeFields", + "; }>" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getComputedFields.$1", + "type": "Object", + "tags": [], + "label": "{ fieldName = ['*'] }", + "description": [], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getComputedFields.$1.fieldName", + "type": "Array", + "tags": [], + "label": "fieldName", + "description": [], + "signature": [ + "string[]" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getRuntimeMappings", + "type": "Function", + "tags": [], + "label": "getRuntimeMappings", + "description": [], + "signature": [ + "() => ", + "MappingRuntimeFields" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.toSpec", + "type": "Function", + "tags": [], + "label": "toSpec", + "description": [ + "\nCreates static representation of the data view." + ], + "signature": [ + "(params?: { fieldParams?: GetFieldsParams | undefined; } | undefined) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewSpec", + "text": "DataViewSpec" + }, + ">" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.toSpec.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.toSpec.$1.fieldParams", + "type": "Object", + "tags": [], + "label": "fieldParams", + "description": [], + "signature": [ + "GetFieldsParams | undefined" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.toMinimalSpec", + "type": "Function", + "tags": [], + "label": "toMinimalSpec", + "description": [ + "\nCreates a minimal static representation of the data view. Fields and popularity scores will be omitted." + ], + "signature": [ + "(params?: { keepFieldAttrs?: (\"customLabel\" | \"customDescription\")[] | undefined; } | undefined) => Omit<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewSpec", + "text": "DataViewSpec" + }, + ", \"fields\">" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.toMinimalSpec.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.toMinimalSpec.$1.keepFieldAttrs", + "type": "Array", + "tags": [], + "label": "keepFieldAttrs", + "description": [], + "signature": [ + "(\"customLabel\" | \"customDescription\")[] | undefined" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.isTSDBMode", + "type": "Function", + "tags": [], + "label": "isTSDBMode", + "description": [ + "\nreturns true if dataview contains TSDB fields" + ], + "signature": [ + "() => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.removeScriptedField", + "type": "Function", + "tags": [], + "label": "removeScriptedField", + "description": [], + "signature": [ + "(fieldName: string) => void" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.removeScriptedField.$1", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.upsertScriptedField", + "type": "Function", + "tags": [], + "label": "upsertScriptedField", + "description": [], + "signature": [ + "(field: ", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.FieldSpec", + "text": "FieldSpec" + }, + ") => void" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.upsertScriptedField.$1", + "type": "CompoundType", + "tags": [], + "label": "field", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.FieldSpec", + "text": "FieldSpec" + } + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.isTimeBased", + "type": "Function", + "tags": [], + "label": "isTimeBased", + "description": [], + "signature": [ + "() => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.isTimeNanosBased", + "type": "Function", + "tags": [], + "label": "isTimeNanosBased", + "description": [], + "signature": [ + "() => Promise" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getTimeField", + "type": "Function", + "tags": [], + "label": "getTimeField", + "description": [], + "signature": [ + "() => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + }, + "> | undefined" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getFieldByName", + "type": "Function", + "tags": [], + "label": "getFieldByName", + "description": [], + "signature": [ + "(name: string, forceRefresh?: boolean) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewField", + "text": "DataViewField" + }, + ">" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getFieldByName.$1", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.getFieldByName.$2", + "type": "boolean", + "tags": [], + "label": "forceRefresh", + "description": [], + "signature": [ + "boolean" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCustomLabel", + "type": "Function", + "tags": [], + "label": "setFieldCustomLabel", + "description": [ + "\nSet field custom label" + ], + "signature": [ + "(fieldName: string, customLabel: string | null | undefined) => void" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCustomLabel.$1", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [ + "name of field to set custom label on" + ], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCustomLabel.$2", + "type": "CompoundType", + "tags": [], + "label": "customLabel", + "description": [ + "custom label value. If undefined, custom label is removed" + ], + "signature": [ + "string | null | undefined" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCount", + "type": "Function", + "tags": [], + "label": "setFieldCount", + "description": [], + "signature": [ + "(fieldName: string, count: number | null | undefined) => void" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCount.$1", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCount.$2", + "type": "CompoundType", + "tags": [], + "label": "count", + "description": [], + "signature": [ + "number | null | undefined" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCustomDescription", + "type": "Function", + "tags": [], + "label": "setFieldCustomDescription", + "description": [], + "signature": [ + "(fieldName: string, customDescription: string | null | undefined) => void" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCustomDescription.$1", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewLazy.setFieldCustomDescription.$2", + "type": "CompoundType", + "tags": [], + "label": "customDescription", + "description": [], + "signature": [ + "string | null | undefined" + ], + "path": "src/plugins/data_views/common/data_views/data_view_lazy.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "dataViews", "id": "def-public.DataViewsApiClient", @@ -3501,6 +4353,46 @@ ], "returnComment": [] }, + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsService.getDataViewLazyFromCache", + "type": "Function", + "tags": [], + "label": "getDataViewLazyFromCache", + "description": [], + "signature": [ + "(id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-public.DataViewsService.getDataViewLazyFromCache.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "dataViews", "id": "def-public.DataViewsService.get", @@ -9435,6 +10327,46 @@ ], "returnComment": [] }, + { + "parentPluginId": "dataViews", + "id": "def-server.DataViewsService.getDataViewLazyFromCache", + "type": "Function", + "tags": [], + "label": "getDataViewLazyFromCache", + "description": [], + "signature": [ + "(id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-server.DataViewsService.getDataViewLazyFromCache.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "dataViews", "id": "def-server.DataViewsService.get", @@ -19550,6 +20482,46 @@ ], "returnComment": [] }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsService.getDataViewLazyFromCache", + "type": "Function", + "tags": [], + "label": "getDataViewLazyFromCache", + "description": [], + "signature": [ + "(id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsService.getDataViewLazyFromCache.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "dataViews", "id": "def-common.DataViewsService.get", @@ -22987,8 +23959,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" + "section": "def-common.AbstractDataView", + "text": "AbstractDataView" }, ", saveAttempts?: number | undefined, ignoreErrors?: boolean | undefined, displayErrors?: boolean | undefined) => Promise" ], @@ -23010,8 +23982,8 @@ "pluginId": "dataViews", "scope": "common", "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" + "section": "def-common.AbstractDataView", + "text": "AbstractDataView" } ], "path": "src/plugins/data_views/common/data_views/data_views.ts", @@ -23293,6 +24265,46 @@ ], "returnComment": [] }, + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsServicePublicMethods.getDataViewLazyFromCache", + "type": "Function", + "tags": [], + "label": "getDataViewLazyFromCache", + "description": [], + "signature": [ + "(id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "dataViews", + "id": "def-common.DataViewsServicePublicMethods.getDataViewLazyFromCache.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "string" + ], + "path": "src/plugins/data_views/common/data_views/data_views.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "dataViews", "id": "def-common.DataViewsServicePublicMethods.createDataViewLazy", @@ -26046,7 +27058,15 @@ "section": "def-common.DataViewLazy", "text": "DataViewLazy" }, - ">; createDataViewLazy: (spec: ", + ">; getDataViewLazyFromCache: (id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>; createDataViewLazy: (spec: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index c43edda5dc15f..b9df8d42d41c7 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1170 | 0 | 401 | 3 | +| 1224 | 0 | 443 | 3 | ## Client diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 60fd3728a2b75..6f7396a898dff 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index 7b52ecde92ba3..3bcf1d1e8bdfb 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index e7d87a3303990..9b377d6c3f83a 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -66,6 +66,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | securitySolution | - | | | @kbn/monaco, securitySolution | - | +| | cloudSecurityPosture, securitySolution | - | | | fleet, exploratoryView, osquery, synthetics | - | | | alerting, observabilityAIAssistant, fleet, cloudSecurityPosture, enterpriseSearch, serverlessSearch, transform, upgradeAssistant, apm, entityManager, synthetics, security | - | | | cloudChat | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 95138e05a1077..d0b2daa0c4269 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -599,6 +599,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [setup_routes.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts#:~:text=authc), [setup_routes.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts#:~:text=authc) | - | | | [csp_benchmark_rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_benchmark_rule.ts#:~:text=migrations) | - | | | [csp_benchmark_rule.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_benchmark_rule.ts#:~:text=schemas), [csp_settings.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_settings.ts#:~:text=schemas) | - | +| | [cloud_security_data_table.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/cloud_security_data_table.tsx#:~:text=externalControlColumns) | - | @@ -1391,6 +1392,7 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | | [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/blocklist/constants.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_NAME), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/blocklist/constants.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/blocklists/index.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_NAME), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/blocklists/index.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_NAME) | - | | | [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/blocklist/constants.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION), [constants.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/management/pages/blocklist/constants.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/blocklists/index.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION), [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/scripts/endpoint/blocklists/index.ts#:~:text=ENDPOINT_BLOCKLISTS_LIST_DESCRIPTION) | - | | | [use_colors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/resolver/view/use_colors.ts#:~:text=darkMode), [use_colors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/resolver/view/use_colors.ts#:~:text=darkMode), [use_colors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/resolver/view/use_colors.ts#:~:text=darkMode), [use_colors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/resolver/view/use_colors.ts#:~:text=darkMode), [use_colors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/resolver/view/use_colors.ts#:~:text=darkMode) | - | +| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx#:~:text=externalControlColumns) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index cb74d1d48db21..5606cb16ee691 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 8e383a60eb546..5ea0d01252fcb 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 30bba67a21b82..2cd6f2880551f 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 882488ff88558..306ed6475304b 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index 9b02cbb2f47d5..b497b8bdef188 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 010b46b096b45..a15f430b74bc5 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 8120b8965f00d..7735ad881e1e1 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index ff4db94fd499e..2f366ce07e0ae 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -9679,7 +9679,7 @@ "section": "def-public.PublishesPhaseEvents", "text": "PublishesPhaseEvents" }, - ",", + ",Partial<", { "pluginId": "@kbn/presentation-publishing", "scope": "public", @@ -9687,7 +9687,7 @@ "section": "def-public.PublishesUnsavedChanges", "text": "PublishesUnsavedChanges" }, - ",", + ">,", { "pluginId": "@kbn/presentation-containers", "scope": "public", diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 792451c78d8d6..64cc9677b665e 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-08-06 +date: 2024-08-09 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 15f5d1fa52a3e..b22f23a7cd605 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 8713b7d4501da..27a1c08396cff 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 5e9f87ee4c64e..97a40e4c49669 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/entities_data_access.devdocs.json b/api_docs/entities_data_access.devdocs.json new file mode 100644 index 0000000000000..26fd0abf8e82b --- /dev/null +++ b/api_docs/entities_data_access.devdocs.json @@ -0,0 +1,43 @@ +{ + "id": "entitiesDataAccess", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "start": { + "parentPluginId": "entitiesDataAccess", + "id": "def-server.EntitiesDataAccessPluginStart", + "type": "Type", + "tags": [], + "label": "EntitiesDataAccessPluginStart", + "description": [], + "signature": [ + "void" + ], + "path": "x-pack/plugins/observability_solution/entities_data_access/server/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "lifecycle": "start", + "initialIsOpen": true + } + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/entities_data_access.mdx b/api_docs/entities_data_access.mdx new file mode 100644 index 0000000000000..60fa8fb1a65b5 --- /dev/null +++ b/api_docs/entities_data_access.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: kibEntitiesDataAccessPluginApi +slug: /kibana-dev-docs/api/entitiesDataAccess +title: "entitiesDataAccess" +image: https://source.unsplash.com/400x175/?github +description: API docs for the entitiesDataAccess plugin +date: 2024-08-09 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entitiesDataAccess'] +--- +import entitiesDataAccessObj from './entities_data_access.devdocs.json'; + + + +Contact [@elastic/obs-entities](https://github.com/orgs/elastic/teams/obs-entities) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 2 | 0 | + +## Server + +### Start + + diff --git a/api_docs/entity_manager.mdx b/api_docs/entity_manager.mdx index d709458747ee9..319f97c72fe77 100644 --- a/api_docs/entity_manager.mdx +++ b/api_docs/entity_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entityManager title: "entityManager" image: https://source.unsplash.com/400x175/?github description: API docs for the entityManager plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entityManager'] --- import entityManagerObj from './entity_manager.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 0fe83a5a937a9..eb096d199cf08 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/esql.mdx b/api_docs/esql.mdx index 78ab3dd17c5e4..d8c687374cb97 100644 --- a/api_docs/esql.mdx +++ b/api_docs/esql.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esql title: "esql" image: https://source.unsplash.com/400x175/?github description: API docs for the esql plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esql'] --- import esqlObj from './esql.devdocs.json'; diff --git a/api_docs/esql_data_grid.mdx b/api_docs/esql_data_grid.mdx index b0d48943a0a47..886ac72ecd7cb 100644 --- a/api_docs/esql_data_grid.mdx +++ b/api_docs/esql_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esqlDataGrid title: "esqlDataGrid" image: https://source.unsplash.com/400x175/?github description: API docs for the esqlDataGrid plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esqlDataGrid'] --- import esqlDataGridObj from './esql_data_grid.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 091077d2cfbd8..a026c486ebcd6 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-08-06 +date: 2024-08-09 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 9e5dd509b59d1..05fb8b1ac4fa7 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-08-06 +date: 2024-08-09 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 04ef0cd37506e..a5e73b5043fdf 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 7a9f16fbf05a6..69ac02b47f290 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 6b08086c58d65..c83f8253e1429 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-08-06 +date: 2024-08-09 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 e973d03f2d740..16f8d3bf1e5a1 100644 --- a/api_docs/expression_gauge.devdocs.json +++ b/api_docs/expression_gauge.devdocs.json @@ -708,7 +708,7 @@ "label": "labelMajorMode", "description": [], "signature": [ - "\"none\" | \"auto\" | \"custom\"" + "\"none\" | \"custom\" | \"auto\"" ], "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts", "deprecated": false, @@ -1143,7 +1143,7 @@ "label": "GaugeLabelMajorMode", "description": [], "signature": [ - "\"none\" | \"auto\" | \"custom\"" + "\"none\" | \"custom\" | \"auto\"" ], "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts", "deprecated": false, diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index ee961f76581bf..8c67a0eb73b22 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 5650e43859548..b2e1bead027c2 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index e6bb096df5005..f982fe3a16ed6 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 6ad2a43a4b967..ea51a34cf01ab 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index fc58097c0a3b6..47d9999fd10f6 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 99fe9f6857c0c..366f0319b4be2 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 3da6a22368029..dd2a90dbe6766 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 5a3feb3af4ca3..ce3fc533ede27 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-08-06 +date: 2024-08-09 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 b63bb1a966c47..5112e2ae86a76 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 9c316d71da331..0f75c5f89517a 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index bb7ce828bcc8e..c44d3e19563c7 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 14eaedd7d576b..005250d67dd89 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index c05e7ee587e05..741974c95b2aa 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-08-06 +date: 2024-08-09 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 ae9befa77ada3..780c6ee08af01 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-08-06 +date: 2024-08-09 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 68dbec97c86d7..ed4b692d9c523 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/fields_metadata.devdocs.json b/api_docs/fields_metadata.devdocs.json index 1c1620c2c72d2..b328d61dc2d34 100644 --- a/api_docs/fields_metadata.devdocs.json +++ b/api_docs/fields_metadata.devdocs.json @@ -635,7 +635,7 @@ "label": "FieldName", "description": [], "signature": [ - "\"@timestamp\" | \"event.sequence\" | \"event.start\" | \"event.end\" | \"event.provider\" | \"event.duration\" | \"event.action\" | \"message\" | \"event.outcome\" | \"tags\" | \"event.kind\" | \"event.original\" | \"agent.name\" | \"container.id\" | \"host.name\" | \"labels\" | \"service.environment\" | \"service.name\" | \"ecs.version\" | \"agent.build.original\" | \"agent.ephemeral_id\" | \"agent.id\" | \"agent.type\" | \"agent.version\" | \"client.address\" | \"client.as.number\" | \"client.as.organization.name\" | \"client.bytes\" | \"client.domain\" | \"client.geo.city_name\" | \"client.geo.continent_code\" | \"client.geo.continent_name\" | \"client.geo.country_iso_code\" | \"client.geo.country_name\" | \"client.geo.location\" | \"client.geo.name\" | \"client.geo.postal_code\" | \"client.geo.region_iso_code\" | \"client.geo.region_name\" | \"client.geo.timezone\" | \"client.ip\" | \"client.mac\" | \"client.nat.ip\" | \"client.nat.port\" | \"client.packets\" | \"client.port\" | \"client.registered_domain\" | \"client.subdomain\" | \"client.top_level_domain\" | \"client.user.domain\" | \"client.user.email\" | \"client.user.full_name\" | \"client.user.group.domain\" | \"client.user.group.id\" | \"client.user.group.name\" | \"client.user.hash\" | \"client.user.id\" | \"client.user.name\" | \"client.user.roles\" | \"cloud.account.id\" | \"cloud.account.name\" | \"cloud.availability_zone\" | \"cloud.instance.id\" | \"cloud.instance.name\" | \"cloud.machine.type\" | \"cloud.origin.account.id\" | \"cloud.origin.account.name\" | \"cloud.origin.availability_zone\" | \"cloud.origin.instance.id\" | \"cloud.origin.instance.name\" | \"cloud.origin.machine.type\" | \"cloud.origin.project.id\" | \"cloud.origin.project.name\" | \"cloud.origin.provider\" | \"cloud.origin.region\" | \"cloud.origin.service.name\" | \"cloud.project.id\" | \"cloud.project.name\" | \"cloud.provider\" | \"cloud.region\" | \"cloud.service.name\" | \"cloud.target.account.id\" | \"cloud.target.account.name\" | \"cloud.target.availability_zone\" | \"cloud.target.instance.id\" | \"cloud.target.instance.name\" | \"cloud.target.machine.type\" | \"cloud.target.project.id\" | \"cloud.target.project.name\" | \"cloud.target.provider\" | \"cloud.target.region\" | \"cloud.target.service.name\" | \"container.cpu.usage\" | \"container.disk.read.bytes\" | \"container.disk.write.bytes\" | \"container.image.hash.all\" | \"container.image.name\" | \"container.image.tag\" | \"container.labels\" | \"container.memory.usage\" | \"container.name\" | \"container.network.egress.bytes\" | \"container.network.ingress.bytes\" | \"container.runtime\" | \"container.security_context.privileged\" | \"destination.address\" | \"destination.as.number\" | \"destination.as.organization.name\" | \"destination.bytes\" | \"destination.domain\" | \"destination.geo.city_name\" | \"destination.geo.continent_code\" | \"destination.geo.continent_name\" | \"destination.geo.country_iso_code\" | \"destination.geo.country_name\" | \"destination.geo.location\" | \"destination.geo.name\" | \"destination.geo.postal_code\" | \"destination.geo.region_iso_code\" | \"destination.geo.region_name\" | \"destination.geo.timezone\" | \"destination.ip\" | \"destination.mac\" | \"destination.nat.ip\" | \"destination.nat.port\" | \"destination.packets\" | \"destination.port\" | \"destination.registered_domain\" | \"destination.subdomain\" | \"destination.top_level_domain\" | \"destination.user.domain\" | \"destination.user.email\" | \"destination.user.full_name\" | \"destination.user.group.domain\" | \"destination.user.group.id\" | \"destination.user.group.name\" | \"destination.user.hash\" | \"destination.user.id\" | \"destination.user.name\" | \"destination.user.roles\" | \"device.id\" | \"device.manufacturer\" | \"device.model.identifier\" | \"device.model.name\" | \"dll.code_signature.digest_algorithm\" | \"dll.code_signature.exists\" | \"dll.code_signature.signing_id\" | \"dll.code_signature.status\" | \"dll.code_signature.subject_name\" | \"dll.code_signature.team_id\" | \"dll.code_signature.timestamp\" | \"dll.code_signature.trusted\" | \"dll.code_signature.valid\" | \"dll.hash.md5\" | \"dll.hash.sha1\" | \"dll.hash.sha256\" | \"dll.hash.sha384\" | \"dll.hash.sha512\" | \"dll.hash.ssdeep\" | \"dll.hash.tlsh\" | \"dll.name\" | \"dll.path\" | \"dll.pe.architecture\" | \"dll.pe.company\" | \"dll.pe.description\" | \"dll.pe.file_version\" | \"dll.pe.go_import_hash\" | \"dll.pe.go_imports\" | \"dll.pe.go_imports_names_entropy\" | \"dll.pe.go_imports_names_var_entropy\" | \"dll.pe.go_stripped\" | \"dll.pe.imphash\" | \"dll.pe.import_hash\" | \"dll.pe.imports\" | \"dll.pe.imports_names_entropy\" | \"dll.pe.imports_names_var_entropy\" | \"dll.pe.original_file_name\" | \"dll.pe.pehash\" | \"dll.pe.product\" | \"dll.pe.sections\" | \"dns.answers\" | \"dns.header_flags\" | \"dns.id\" | \"dns.op_code\" | \"dns.question.class\" | \"dns.question.name\" | \"dns.question.registered_domain\" | \"dns.question.subdomain\" | \"dns.question.top_level_domain\" | \"dns.question.type\" | \"dns.resolved_ip\" | \"dns.response_code\" | \"dns.type\" | \"email.attachments\" | \"file.extension\" | \"file.hash.md5\" | \"file.hash.sha1\" | \"file.hash.sha256\" | \"file.hash.sha384\" | \"file.hash.sha512\" | \"file.hash.ssdeep\" | \"file.hash.tlsh\" | \"file.mime_type\" | \"file.name\" | \"file.size\" | \"email.bcc.address\" | \"email.cc.address\" | \"email.content_type\" | \"email.delivery_timestamp\" | \"email.direction\" | \"email.from.address\" | \"email.local_id\" | \"email.message_id\" | \"email.origination_timestamp\" | \"email.reply_to.address\" | \"email.sender.address\" | \"email.subject\" | \"email.to.address\" | \"email.x_mailer\" | \"error.code\" | \"error.id\" | \"error.message\" | \"error.stack_trace\" | \"error.type\" | \"event.agent_id_status\" | \"event.category\" | \"event.code\" | \"event.created\" | \"event.dataset\" | \"event.hash\" | \"event.id\" | \"event.ingested\" | \"event.module\" | \"event.reason\" | \"event.reference\" | \"event.risk_score\" | \"event.risk_score_norm\" | \"event.severity\" | \"event.timezone\" | \"event.type\" | \"event.url\" | \"faas.coldstart\" | \"faas.execution\" | \"faas.id\" | \"faas.name\" | \"faas.version\" | \"file.accessed\" | \"file.attributes\" | \"file.code_signature.digest_algorithm\" | \"file.code_signature.exists\" | \"file.code_signature.signing_id\" | \"file.code_signature.status\" | \"file.code_signature.subject_name\" | \"file.code_signature.team_id\" | \"file.code_signature.timestamp\" | \"file.code_signature.trusted\" | \"file.code_signature.valid\" | \"file.created\" | \"file.ctime\" | \"file.device\" | \"file.directory\" | \"file.drive_letter\" | \"file.elf.architecture\" | \"file.elf.byte_order\" | \"file.elf.cpu_type\" | \"file.elf.creation_date\" | \"file.elf.exports\" | \"file.elf.go_import_hash\" | \"file.elf.go_imports\" | \"file.elf.go_imports_names_entropy\" | \"file.elf.go_imports_names_var_entropy\" | \"file.elf.go_stripped\" | \"file.elf.header.abi_version\" | \"file.elf.header.class\" | \"file.elf.header.data\" | \"file.elf.header.entrypoint\" | \"file.elf.header.object_version\" | \"file.elf.header.os_abi\" | \"file.elf.header.type\" | \"file.elf.header.version\" | \"file.elf.import_hash\" | \"file.elf.imports\" | \"file.elf.imports_names_entropy\" | \"file.elf.imports_names_var_entropy\" | \"file.elf.sections\" | \"file.elf.segments\" | \"file.elf.shared_libraries\" | \"file.elf.telfhash\" | \"file.fork_name\" | \"file.gid\" | \"file.group\" | \"file.inode\" | \"file.macho.go_import_hash\" | \"file.macho.go_imports\" | \"file.macho.go_imports_names_entropy\" | \"file.macho.go_imports_names_var_entropy\" | \"file.macho.go_stripped\" | \"file.macho.import_hash\" | \"file.macho.imports\" | \"file.macho.imports_names_entropy\" | \"file.macho.imports_names_var_entropy\" | \"file.macho.sections\" | \"file.macho.symhash\" | \"file.mode\" | \"file.mtime\" | \"file.owner\" | \"file.path\" | \"file.pe.architecture\" | \"file.pe.company\" | \"file.pe.description\" | \"file.pe.file_version\" | \"file.pe.go_import_hash\" | \"file.pe.go_imports\" | \"file.pe.go_imports_names_entropy\" | \"file.pe.go_imports_names_var_entropy\" | \"file.pe.go_stripped\" | \"file.pe.imphash\" | \"file.pe.import_hash\" | \"file.pe.imports\" | \"file.pe.imports_names_entropy\" | \"file.pe.imports_names_var_entropy\" | \"file.pe.original_file_name\" | \"file.pe.pehash\" | \"file.pe.product\" | \"file.pe.sections\" | \"file.target_path\" | \"file.type\" | \"file.uid\" | \"file.x509.alternative_names\" | \"file.x509.issuer.common_name\" | \"file.x509.issuer.country\" | \"file.x509.issuer.distinguished_name\" | \"file.x509.issuer.locality\" | \"file.x509.issuer.organization\" | \"file.x509.issuer.organizational_unit\" | \"file.x509.issuer.state_or_province\" | \"file.x509.not_after\" | \"file.x509.not_before\" | \"file.x509.public_key_algorithm\" | \"file.x509.public_key_curve\" | \"file.x509.public_key_exponent\" | \"file.x509.public_key_size\" | \"file.x509.serial_number\" | \"file.x509.signature_algorithm\" | \"file.x509.subject.common_name\" | \"file.x509.subject.country\" | \"file.x509.subject.distinguished_name\" | \"file.x509.subject.locality\" | \"file.x509.subject.organization\" | \"file.x509.subject.organizational_unit\" | \"file.x509.subject.state_or_province\" | \"file.x509.version_number\" | \"group.domain\" | \"group.id\" | \"group.name\" | \"host.architecture\" | \"host.boot.id\" | \"host.cpu.usage\" | \"host.disk.read.bytes\" | \"host.disk.write.bytes\" | \"host.domain\" | \"host.geo.city_name\" | \"host.geo.continent_code\" | \"host.geo.continent_name\" | \"host.geo.country_iso_code\" | \"host.geo.country_name\" | \"host.geo.location\" | \"host.geo.name\" | \"host.geo.postal_code\" | \"host.geo.region_iso_code\" | \"host.geo.region_name\" | \"host.geo.timezone\" | \"host.hostname\" | \"host.id\" | \"host.ip\" | \"host.mac\" | \"host.network.egress.bytes\" | \"host.network.egress.packets\" | \"host.network.ingress.bytes\" | \"host.network.ingress.packets\" | \"host.os.family\" | \"host.os.full\" | \"host.os.kernel\" | \"host.os.name\" | \"host.os.platform\" | \"host.os.type\" | \"host.os.version\" | \"host.pid_ns_ino\" | \"host.risk.calculated_level\" | \"host.risk.calculated_score\" | \"host.risk.calculated_score_norm\" | \"host.risk.static_level\" | \"host.risk.static_score\" | \"host.risk.static_score_norm\" | \"host.type\" | \"host.uptime\" | \"http.request.body.bytes\" | \"http.request.body.content\" | \"http.request.bytes\" | \"http.request.id\" | \"http.request.method\" | \"http.request.mime_type\" | \"http.request.referrer\" | \"http.response.body.bytes\" | \"http.response.body.content\" | \"http.response.bytes\" | \"http.response.mime_type\" | \"http.response.status_code\" | \"http.version\" | \"log.file.path\" | \"log.level\" | \"log.logger\" | \"log.origin.file.line\" | \"log.origin.file.name\" | \"log.origin.function\" | \"log.syslog\" | \"network.application\" | \"network.bytes\" | \"network.community_id\" | \"network.direction\" | \"network.forwarded_ip\" | \"network.iana_number\" | \"network.inner\" | \"network.name\" | \"network.packets\" | \"network.protocol\" | \"network.transport\" | \"network.type\" | \"network.vlan.id\" | \"network.vlan.name\" | \"observer.egress\" | \"observer.geo.city_name\" | \"observer.geo.continent_code\" | \"observer.geo.continent_name\" | \"observer.geo.country_iso_code\" | \"observer.geo.country_name\" | \"observer.geo.location\" | \"observer.geo.name\" | \"observer.geo.postal_code\" | \"observer.geo.region_iso_code\" | \"observer.geo.region_name\" | \"observer.geo.timezone\" | \"observer.hostname\" | \"observer.ingress\" | \"observer.ip\" | \"observer.mac\" | \"observer.name\" | \"observer.os.family\" | \"observer.os.full\" | \"observer.os.kernel\" | \"observer.os.name\" | \"observer.os.platform\" | \"observer.os.type\" | \"observer.os.version\" | \"observer.product\" | \"observer.serial_number\" | \"observer.type\" | \"observer.vendor\" | \"observer.version\" | \"orchestrator.api_version\" | \"orchestrator.cluster.id\" | \"orchestrator.cluster.name\" | \"orchestrator.cluster.url\" | \"orchestrator.cluster.version\" | \"orchestrator.namespace\" | \"orchestrator.organization\" | \"orchestrator.resource.annotation\" | \"orchestrator.resource.id\" | \"orchestrator.resource.ip\" | \"orchestrator.resource.label\" | \"orchestrator.resource.name\" | \"orchestrator.resource.parent.type\" | \"orchestrator.resource.type\" | \"orchestrator.type\" | \"organization.id\" | \"organization.name\" | \"package.architecture\" | \"package.build_version\" | \"package.checksum\" | \"package.description\" | \"package.install_scope\" | \"package.installed\" | \"package.license\" | \"package.name\" | \"package.path\" | \"package.reference\" | \"package.size\" | \"package.type\" | \"package.version\" | \"process.args\" | \"process.args_count\" | \"process.code_signature.digest_algorithm\" | \"process.code_signature.exists\" | \"process.code_signature.signing_id\" | \"process.code_signature.status\" | \"process.code_signature.subject_name\" | \"process.code_signature.team_id\" | \"process.code_signature.timestamp\" | \"process.code_signature.trusted\" | \"process.code_signature.valid\" | \"process.command_line\" | \"process.elf.architecture\" | \"process.elf.byte_order\" | \"process.elf.cpu_type\" | \"process.elf.creation_date\" | \"process.elf.exports\" | \"process.elf.go_import_hash\" | \"process.elf.go_imports\" | \"process.elf.go_imports_names_entropy\" | \"process.elf.go_imports_names_var_entropy\" | \"process.elf.go_stripped\" | \"process.elf.header.abi_version\" | \"process.elf.header.class\" | \"process.elf.header.data\" | \"process.elf.header.entrypoint\" | \"process.elf.header.object_version\" | \"process.elf.header.os_abi\" | \"process.elf.header.type\" | \"process.elf.header.version\" | \"process.elf.import_hash\" | \"process.elf.imports\" | \"process.elf.imports_names_entropy\" | \"process.elf.imports_names_var_entropy\" | \"process.elf.sections\" | \"process.elf.segments\" | \"process.elf.shared_libraries\" | \"process.elf.telfhash\" | \"process.end\" | \"process.entity_id\" | \"process.entry_leader.args\" | \"process.entry_leader.args_count\" | \"process.entry_leader.attested_groups.name\" | \"process.entry_leader.attested_user.id\" | \"process.entry_leader.attested_user.name\" | \"process.entry_leader.command_line\" | \"process.entry_leader.entity_id\" | \"process.entry_leader.entry_meta.source.ip\" | \"process.entry_leader.entry_meta.type\" | \"process.entry_leader.executable\" | \"process.entry_leader.group.id\" | \"process.entry_leader.group.name\" | \"process.entry_leader.interactive\" | \"process.entry_leader.name\" | \"process.entry_leader.parent.entity_id\" | \"process.entry_leader.parent.pid\" | \"process.entry_leader.parent.session_leader.entity_id\" | \"process.entry_leader.parent.session_leader.pid\" | \"process.entry_leader.parent.session_leader.start\" | \"process.entry_leader.parent.session_leader.vpid\" | \"process.entry_leader.parent.start\" | \"process.entry_leader.parent.vpid\" | \"process.entry_leader.pid\" | \"process.entry_leader.real_group.id\" | \"process.entry_leader.real_group.name\" | \"process.entry_leader.real_user.id\" | \"process.entry_leader.real_user.name\" | \"process.entry_leader.same_as_process\" | \"process.entry_leader.saved_group.id\" | \"process.entry_leader.saved_group.name\" | \"process.entry_leader.saved_user.id\" | \"process.entry_leader.saved_user.name\" | \"process.entry_leader.start\" | \"process.entry_leader.supplemental_groups.id\" | \"process.entry_leader.supplemental_groups.name\" | \"process.entry_leader.tty\" | \"process.entry_leader.user.id\" | \"process.entry_leader.user.name\" | \"process.entry_leader.vpid\" | \"process.entry_leader.working_directory\" | \"process.env_vars\" | \"process.executable\" | \"process.exit_code\" | \"process.group_leader.args\" | \"process.group_leader.args_count\" | \"process.group_leader.command_line\" | \"process.group_leader.entity_id\" | \"process.group_leader.executable\" | \"process.group_leader.group.id\" | \"process.group_leader.group.name\" | \"process.group_leader.interactive\" | \"process.group_leader.name\" | \"process.group_leader.pid\" | \"process.group_leader.real_group.id\" | \"process.group_leader.real_group.name\" | \"process.group_leader.real_user.id\" | \"process.group_leader.real_user.name\" | \"process.group_leader.same_as_process\" | \"process.group_leader.saved_group.id\" | \"process.group_leader.saved_group.name\" | \"process.group_leader.saved_user.id\" | \"process.group_leader.saved_user.name\" | \"process.group_leader.start\" | \"process.group_leader.supplemental_groups.id\" | \"process.group_leader.supplemental_groups.name\" | \"process.group_leader.tty\" | \"process.group_leader.user.id\" | \"process.group_leader.user.name\" | \"process.group_leader.vpid\" | \"process.group_leader.working_directory\" | \"process.hash.md5\" | \"process.hash.sha1\" | \"process.hash.sha256\" | \"process.hash.sha384\" | \"process.hash.sha512\" | \"process.hash.ssdeep\" | \"process.hash.tlsh\" | \"process.interactive\" | \"process.io\" | \"process.macho.go_import_hash\" | \"process.macho.go_imports\" | \"process.macho.go_imports_names_entropy\" | \"process.macho.go_imports_names_var_entropy\" | \"process.macho.go_stripped\" | \"process.macho.import_hash\" | \"process.macho.imports\" | \"process.macho.imports_names_entropy\" | \"process.macho.imports_names_var_entropy\" | \"process.macho.sections\" | \"process.macho.symhash\" | \"process.name\" | \"process.parent.args\" | \"process.parent.args_count\" | \"process.parent.code_signature.digest_algorithm\" | \"process.parent.code_signature.exists\" | \"process.parent.code_signature.signing_id\" | \"process.parent.code_signature.status\" | \"process.parent.code_signature.subject_name\" | \"process.parent.code_signature.team_id\" | \"process.parent.code_signature.timestamp\" | \"process.parent.code_signature.trusted\" | \"process.parent.code_signature.valid\" | \"process.parent.command_line\" | \"process.parent.elf.architecture\" | \"process.parent.elf.byte_order\" | \"process.parent.elf.cpu_type\" | \"process.parent.elf.creation_date\" | \"process.parent.elf.exports\" | \"process.parent.elf.go_import_hash\" | \"process.parent.elf.go_imports\" | \"process.parent.elf.go_imports_names_entropy\" | \"process.parent.elf.go_imports_names_var_entropy\" | \"process.parent.elf.go_stripped\" | \"process.parent.elf.header.abi_version\" | \"process.parent.elf.header.class\" | \"process.parent.elf.header.data\" | \"process.parent.elf.header.entrypoint\" | \"process.parent.elf.header.object_version\" | \"process.parent.elf.header.os_abi\" | \"process.parent.elf.header.type\" | \"process.parent.elf.header.version\" | \"process.parent.elf.import_hash\" | \"process.parent.elf.imports\" | \"process.parent.elf.imports_names_entropy\" | \"process.parent.elf.imports_names_var_entropy\" | \"process.parent.elf.sections\" | \"process.parent.elf.segments\" | \"process.parent.elf.shared_libraries\" | \"process.parent.elf.telfhash\" | \"process.parent.end\" | \"process.parent.entity_id\" | \"process.parent.executable\" | \"process.parent.exit_code\" | \"process.parent.group.id\" | \"process.parent.group.name\" | \"process.parent.group_leader.entity_id\" | \"process.parent.group_leader.pid\" | \"process.parent.group_leader.start\" | \"process.parent.group_leader.vpid\" | \"process.parent.hash.md5\" | \"process.parent.hash.sha1\" | \"process.parent.hash.sha256\" | \"process.parent.hash.sha384\" | \"process.parent.hash.sha512\" | \"process.parent.hash.ssdeep\" | \"process.parent.hash.tlsh\" | \"process.parent.interactive\" | \"process.parent.macho.go_import_hash\" | \"process.parent.macho.go_imports\" | \"process.parent.macho.go_imports_names_entropy\" | \"process.parent.macho.go_imports_names_var_entropy\" | \"process.parent.macho.go_stripped\" | \"process.parent.macho.import_hash\" | \"process.parent.macho.imports\" | \"process.parent.macho.imports_names_entropy\" | \"process.parent.macho.imports_names_var_entropy\" | \"process.parent.macho.sections\" | \"process.parent.macho.symhash\" | \"process.parent.name\" | \"process.parent.pe.architecture\" | \"process.parent.pe.company\" | \"process.parent.pe.description\" | \"process.parent.pe.file_version\" | \"process.parent.pe.go_import_hash\" | \"process.parent.pe.go_imports\" | \"process.parent.pe.go_imports_names_entropy\" | \"process.parent.pe.go_imports_names_var_entropy\" | \"process.parent.pe.go_stripped\" | \"process.parent.pe.imphash\" | \"process.parent.pe.import_hash\" | \"process.parent.pe.imports\" | \"process.parent.pe.imports_names_entropy\" | \"process.parent.pe.imports_names_var_entropy\" | \"process.parent.pe.original_file_name\" | \"process.parent.pe.pehash\" | \"process.parent.pe.product\" | \"process.parent.pe.sections\" | \"process.parent.pgid\" | \"process.parent.pid\" | \"process.parent.real_group.id\" | \"process.parent.real_group.name\" | \"process.parent.real_user.id\" | \"process.parent.real_user.name\" | \"process.parent.saved_group.id\" | \"process.parent.saved_group.name\" | \"process.parent.saved_user.id\" | \"process.parent.saved_user.name\" | \"process.parent.start\" | \"process.parent.supplemental_groups.id\" | \"process.parent.supplemental_groups.name\" | \"process.parent.thread.capabilities.effective\" | \"process.parent.thread.capabilities.permitted\" | \"process.parent.thread.id\" | \"process.parent.thread.name\" | \"process.parent.title\" | \"process.parent.tty\" | \"process.parent.uptime\" | \"process.parent.user.id\" | \"process.parent.user.name\" | \"process.parent.vpid\" | \"process.parent.working_directory\" | \"process.pe.architecture\" | \"process.pe.company\" | \"process.pe.description\" | \"process.pe.file_version\" | \"process.pe.go_import_hash\" | \"process.pe.go_imports\" | \"process.pe.go_imports_names_entropy\" | \"process.pe.go_imports_names_var_entropy\" | \"process.pe.go_stripped\" | \"process.pe.imphash\" | \"process.pe.import_hash\" | \"process.pe.imports\" | \"process.pe.imports_names_entropy\" | \"process.pe.imports_names_var_entropy\" | \"process.pe.original_file_name\" | \"process.pe.pehash\" | \"process.pe.product\" | \"process.pe.sections\" | \"process.pgid\" | \"process.pid\" | \"process.previous.args\" | \"process.previous.args_count\" | \"process.previous.executable\" | \"process.real_group.id\" | \"process.real_group.name\" | \"process.real_user.id\" | \"process.real_user.name\" | \"process.saved_group.id\" | \"process.saved_group.name\" | \"process.saved_user.id\" | \"process.saved_user.name\" | \"process.session_leader.args\" | \"process.session_leader.args_count\" | \"process.session_leader.command_line\" | \"process.session_leader.entity_id\" | \"process.session_leader.executable\" | \"process.session_leader.group.id\" | \"process.session_leader.group.name\" | \"process.session_leader.interactive\" | \"process.session_leader.name\" | \"process.session_leader.parent.entity_id\" | \"process.session_leader.parent.pid\" | \"process.session_leader.parent.session_leader.entity_id\" | \"process.session_leader.parent.session_leader.pid\" | \"process.session_leader.parent.session_leader.start\" | \"process.session_leader.parent.session_leader.vpid\" | \"process.session_leader.parent.start\" | \"process.session_leader.parent.vpid\" | \"process.session_leader.pid\" | \"process.session_leader.real_group.id\" | \"process.session_leader.real_group.name\" | \"process.session_leader.real_user.id\" | \"process.session_leader.real_user.name\" | \"process.session_leader.same_as_process\" | \"process.session_leader.saved_group.id\" | \"process.session_leader.saved_group.name\" | \"process.session_leader.saved_user.id\" | \"process.session_leader.saved_user.name\" | \"process.session_leader.start\" | \"process.session_leader.supplemental_groups.id\" | \"process.session_leader.supplemental_groups.name\" | \"process.session_leader.tty\" | \"process.session_leader.user.id\" | \"process.session_leader.user.name\" | \"process.session_leader.vpid\" | \"process.session_leader.working_directory\" | \"process.start\" | \"process.supplemental_groups.id\" | \"process.supplemental_groups.name\" | \"process.thread.capabilities.effective\" | \"process.thread.capabilities.permitted\" | \"process.thread.id\" | \"process.thread.name\" | \"process.title\" | \"process.tty\" | \"process.uptime\" | \"process.user.id\" | \"process.user.name\" | \"process.vpid\" | \"process.working_directory\" | \"registry.data.bytes\" | \"registry.data.strings\" | \"registry.data.type\" | \"registry.hive\" | \"registry.key\" | \"registry.path\" | \"registry.value\" | \"related.hash\" | \"related.hosts\" | \"related.ip\" | \"related.user\" | \"rule.author\" | \"rule.category\" | \"rule.description\" | \"rule.id\" | \"rule.license\" | \"rule.name\" | \"rule.reference\" | \"rule.ruleset\" | \"rule.uuid\" | \"rule.version\" | \"server.address\" | \"server.as.number\" | \"server.as.organization.name\" | \"server.bytes\" | \"server.domain\" | \"server.geo.city_name\" | \"server.geo.continent_code\" | \"server.geo.continent_name\" | \"server.geo.country_iso_code\" | \"server.geo.country_name\" | \"server.geo.location\" | \"server.geo.name\" | \"server.geo.postal_code\" | \"server.geo.region_iso_code\" | \"server.geo.region_name\" | \"server.geo.timezone\" | \"server.ip\" | \"server.mac\" | \"server.nat.ip\" | \"server.nat.port\" | \"server.packets\" | \"server.port\" | \"server.registered_domain\" | \"server.subdomain\" | \"server.top_level_domain\" | \"server.user.domain\" | \"server.user.email\" | \"server.user.full_name\" | \"server.user.group.domain\" | \"server.user.group.id\" | \"server.user.group.name\" | \"server.user.hash\" | \"server.user.id\" | \"server.user.name\" | \"server.user.roles\" | \"service.address\" | \"service.ephemeral_id\" | \"service.id\" | \"service.node.name\" | \"service.node.role\" | \"service.node.roles\" | \"service.origin.address\" | \"service.origin.environment\" | \"service.origin.ephemeral_id\" | \"service.origin.id\" | \"service.origin.name\" | \"service.origin.node.name\" | \"service.origin.node.role\" | \"service.origin.node.roles\" | \"service.origin.state\" | \"service.origin.type\" | \"service.origin.version\" | \"service.state\" | \"service.target.address\" | \"service.target.environment\" | \"service.target.ephemeral_id\" | \"service.target.id\" | \"service.target.name\" | \"service.target.node.name\" | \"service.target.node.role\" | \"service.target.node.roles\" | \"service.target.state\" | \"service.target.type\" | \"service.target.version\" | \"service.type\" | \"service.version\" | \"source.address\" | \"source.as.number\" | \"source.as.organization.name\" | \"source.bytes\" | \"source.domain\" | \"source.geo.city_name\" | \"source.geo.continent_code\" | \"source.geo.continent_name\" | \"source.geo.country_iso_code\" | \"source.geo.country_name\" | \"source.geo.location\" | \"source.geo.name\" | \"source.geo.postal_code\" | \"source.geo.region_iso_code\" | \"source.geo.region_name\" | \"source.geo.timezone\" | \"source.ip\" | \"source.mac\" | \"source.nat.ip\" | \"source.nat.port\" | \"source.packets\" | \"source.port\" | \"source.registered_domain\" | \"source.subdomain\" | \"source.top_level_domain\" | \"source.user.domain\" | \"source.user.email\" | \"source.user.full_name\" | \"source.user.group.domain\" | \"source.user.group.id\" | \"source.user.group.name\" | \"source.user.hash\" | \"source.user.id\" | \"source.user.name\" | \"source.user.roles\" | \"span.id\" | \"threat.enrichments\" | \"threat.feed.dashboard_id\" | \"threat.feed.description\" | \"threat.feed.name\" | \"threat.feed.reference\" | \"threat.framework\" | \"threat.group.alias\" | \"threat.group.id\" | \"threat.group.name\" | \"threat.group.reference\" | \"threat.indicator.as.number\" | \"threat.indicator.as.organization.name\" | \"threat.indicator.confidence\" | \"threat.indicator.description\" | \"threat.indicator.email.address\" | \"threat.indicator.file.accessed\" | \"threat.indicator.file.attributes\" | \"threat.indicator.file.code_signature.digest_algorithm\" | \"threat.indicator.file.code_signature.exists\" | \"threat.indicator.file.code_signature.signing_id\" | \"threat.indicator.file.code_signature.status\" | \"threat.indicator.file.code_signature.subject_name\" | \"threat.indicator.file.code_signature.team_id\" | \"threat.indicator.file.code_signature.timestamp\" | \"threat.indicator.file.code_signature.trusted\" | \"threat.indicator.file.code_signature.valid\" | \"threat.indicator.file.created\" | \"threat.indicator.file.ctime\" | \"threat.indicator.file.device\" | \"threat.indicator.file.directory\" | \"threat.indicator.file.drive_letter\" | \"threat.indicator.file.elf.architecture\" | \"threat.indicator.file.elf.byte_order\" | \"threat.indicator.file.elf.cpu_type\" | \"threat.indicator.file.elf.creation_date\" | \"threat.indicator.file.elf.exports\" | \"threat.indicator.file.elf.go_import_hash\" | \"threat.indicator.file.elf.go_imports\" | \"threat.indicator.file.elf.go_imports_names_entropy\" | \"threat.indicator.file.elf.go_imports_names_var_entropy\" | \"threat.indicator.file.elf.go_stripped\" | \"threat.indicator.file.elf.header.abi_version\" | \"threat.indicator.file.elf.header.class\" | \"threat.indicator.file.elf.header.data\" | \"threat.indicator.file.elf.header.entrypoint\" | \"threat.indicator.file.elf.header.object_version\" | \"threat.indicator.file.elf.header.os_abi\" | \"threat.indicator.file.elf.header.type\" | \"threat.indicator.file.elf.header.version\" | \"threat.indicator.file.elf.import_hash\" | \"threat.indicator.file.elf.imports\" | \"threat.indicator.file.elf.imports_names_entropy\" | \"threat.indicator.file.elf.imports_names_var_entropy\" | \"threat.indicator.file.elf.sections\" | \"threat.indicator.file.elf.segments\" | \"threat.indicator.file.elf.shared_libraries\" | \"threat.indicator.file.elf.telfhash\" | \"threat.indicator.file.extension\" | \"threat.indicator.file.fork_name\" | \"threat.indicator.file.gid\" | \"threat.indicator.file.group\" | \"threat.indicator.file.hash.md5\" | \"threat.indicator.file.hash.sha1\" | \"threat.indicator.file.hash.sha256\" | \"threat.indicator.file.hash.sha384\" | \"threat.indicator.file.hash.sha512\" | \"threat.indicator.file.hash.ssdeep\" | \"threat.indicator.file.hash.tlsh\" | \"threat.indicator.file.inode\" | \"threat.indicator.file.mime_type\" | \"threat.indicator.file.mode\" | \"threat.indicator.file.mtime\" | \"threat.indicator.file.name\" | \"threat.indicator.file.owner\" | \"threat.indicator.file.path\" | \"threat.indicator.file.pe.architecture\" | \"threat.indicator.file.pe.company\" | \"threat.indicator.file.pe.description\" | \"threat.indicator.file.pe.file_version\" | \"threat.indicator.file.pe.go_import_hash\" | \"threat.indicator.file.pe.go_imports\" | \"threat.indicator.file.pe.go_imports_names_entropy\" | \"threat.indicator.file.pe.go_imports_names_var_entropy\" | \"threat.indicator.file.pe.go_stripped\" | \"threat.indicator.file.pe.imphash\" | \"threat.indicator.file.pe.import_hash\" | \"threat.indicator.file.pe.imports\" | \"threat.indicator.file.pe.imports_names_entropy\" | \"threat.indicator.file.pe.imports_names_var_entropy\" | \"threat.indicator.file.pe.original_file_name\" | \"threat.indicator.file.pe.pehash\" | \"threat.indicator.file.pe.product\" | \"threat.indicator.file.pe.sections\" | \"threat.indicator.file.size\" | \"threat.indicator.file.target_path\" | \"threat.indicator.file.type\" | \"threat.indicator.file.uid\" | \"threat.indicator.file.x509.alternative_names\" | \"threat.indicator.file.x509.issuer.common_name\" | \"threat.indicator.file.x509.issuer.country\" | \"threat.indicator.file.x509.issuer.distinguished_name\" | \"threat.indicator.file.x509.issuer.locality\" | \"threat.indicator.file.x509.issuer.organization\" | \"threat.indicator.file.x509.issuer.organizational_unit\" | \"threat.indicator.file.x509.issuer.state_or_province\" | \"threat.indicator.file.x509.not_after\" | \"threat.indicator.file.x509.not_before\" | \"threat.indicator.file.x509.public_key_algorithm\" | \"threat.indicator.file.x509.public_key_curve\" | \"threat.indicator.file.x509.public_key_exponent\" | \"threat.indicator.file.x509.public_key_size\" | \"threat.indicator.file.x509.serial_number\" | \"threat.indicator.file.x509.signature_algorithm\" | \"threat.indicator.file.x509.subject.common_name\" | \"threat.indicator.file.x509.subject.country\" | \"threat.indicator.file.x509.subject.distinguished_name\" | \"threat.indicator.file.x509.subject.locality\" | \"threat.indicator.file.x509.subject.organization\" | \"threat.indicator.file.x509.subject.organizational_unit\" | \"threat.indicator.file.x509.subject.state_or_province\" | \"threat.indicator.file.x509.version_number\" | \"threat.indicator.first_seen\" | \"threat.indicator.geo.city_name\" | \"threat.indicator.geo.continent_code\" | \"threat.indicator.geo.continent_name\" | \"threat.indicator.geo.country_iso_code\" | \"threat.indicator.geo.country_name\" | \"threat.indicator.geo.location\" | \"threat.indicator.geo.name\" | \"threat.indicator.geo.postal_code\" | \"threat.indicator.geo.region_iso_code\" | \"threat.indicator.geo.region_name\" | \"threat.indicator.geo.timezone\" | \"threat.indicator.ip\" | \"threat.indicator.last_seen\" | \"threat.indicator.marking.tlp\" | \"threat.indicator.marking.tlp_version\" | \"threat.indicator.modified_at\" | \"threat.indicator.name\" | \"threat.indicator.port\" | \"threat.indicator.provider\" | \"threat.indicator.reference\" | \"threat.indicator.registry.data.bytes\" | \"threat.indicator.registry.data.strings\" | \"threat.indicator.registry.data.type\" | \"threat.indicator.registry.hive\" | \"threat.indicator.registry.key\" | \"threat.indicator.registry.path\" | \"threat.indicator.registry.value\" | \"threat.indicator.scanner_stats\" | \"threat.indicator.sightings\" | \"threat.indicator.type\" | \"threat.indicator.url.domain\" | \"threat.indicator.url.extension\" | \"threat.indicator.url.fragment\" | \"threat.indicator.url.full\" | \"threat.indicator.url.original\" | \"threat.indicator.url.password\" | \"threat.indicator.url.path\" | \"threat.indicator.url.port\" | \"threat.indicator.url.query\" | \"threat.indicator.url.registered_domain\" | \"threat.indicator.url.scheme\" | \"threat.indicator.url.subdomain\" | \"threat.indicator.url.top_level_domain\" | \"threat.indicator.url.username\" | \"threat.indicator.x509.alternative_names\" | \"threat.indicator.x509.issuer.common_name\" | \"threat.indicator.x509.issuer.country\" | \"threat.indicator.x509.issuer.distinguished_name\" | \"threat.indicator.x509.issuer.locality\" | \"threat.indicator.x509.issuer.organization\" | \"threat.indicator.x509.issuer.organizational_unit\" | \"threat.indicator.x509.issuer.state_or_province\" | \"threat.indicator.x509.not_after\" | \"threat.indicator.x509.not_before\" | \"threat.indicator.x509.public_key_algorithm\" | \"threat.indicator.x509.public_key_curve\" | \"threat.indicator.x509.public_key_exponent\" | \"threat.indicator.x509.public_key_size\" | \"threat.indicator.x509.serial_number\" | \"threat.indicator.x509.signature_algorithm\" | \"threat.indicator.x509.subject.common_name\" | \"threat.indicator.x509.subject.country\" | \"threat.indicator.x509.subject.distinguished_name\" | \"threat.indicator.x509.subject.locality\" | \"threat.indicator.x509.subject.organization\" | \"threat.indicator.x509.subject.organizational_unit\" | \"threat.indicator.x509.subject.state_or_province\" | \"threat.indicator.x509.version_number\" | \"threat.software.alias\" | \"threat.software.id\" | \"threat.software.name\" | \"threat.software.platforms\" | \"threat.software.reference\" | \"threat.software.type\" | \"threat.tactic.id\" | \"threat.tactic.name\" | \"threat.tactic.reference\" | \"threat.technique.id\" | \"threat.technique.name\" | \"threat.technique.reference\" | \"threat.technique.subtechnique.id\" | \"threat.technique.subtechnique.name\" | \"threat.technique.subtechnique.reference\" | \"tls.cipher\" | \"tls.client.certificate\" | \"tls.client.certificate_chain\" | \"tls.client.hash.md5\" | \"tls.client.hash.sha1\" | \"tls.client.hash.sha256\" | \"tls.client.issuer\" | \"tls.client.ja3\" | \"tls.client.not_after\" | \"tls.client.not_before\" | \"tls.client.server_name\" | \"tls.client.subject\" | \"tls.client.supported_ciphers\" | \"tls.client.x509.alternative_names\" | \"tls.client.x509.issuer.common_name\" | \"tls.client.x509.issuer.country\" | \"tls.client.x509.issuer.distinguished_name\" | \"tls.client.x509.issuer.locality\" | \"tls.client.x509.issuer.organization\" | \"tls.client.x509.issuer.organizational_unit\" | \"tls.client.x509.issuer.state_or_province\" | \"tls.client.x509.not_after\" | \"tls.client.x509.not_before\" | \"tls.client.x509.public_key_algorithm\" | \"tls.client.x509.public_key_curve\" | \"tls.client.x509.public_key_exponent\" | \"tls.client.x509.public_key_size\" | \"tls.client.x509.serial_number\" | \"tls.client.x509.signature_algorithm\" | \"tls.client.x509.subject.common_name\" | \"tls.client.x509.subject.country\" | \"tls.client.x509.subject.distinguished_name\" | \"tls.client.x509.subject.locality\" | \"tls.client.x509.subject.organization\" | \"tls.client.x509.subject.organizational_unit\" | \"tls.client.x509.subject.state_or_province\" | \"tls.client.x509.version_number\" | \"tls.curve\" | \"tls.established\" | \"tls.next_protocol\" | \"tls.resumed\" | \"tls.server.certificate\" | \"tls.server.certificate_chain\" | \"tls.server.hash.md5\" | \"tls.server.hash.sha1\" | \"tls.server.hash.sha256\" | \"tls.server.issuer\" | \"tls.server.ja3s\" | \"tls.server.not_after\" | \"tls.server.not_before\" | \"tls.server.subject\" | \"tls.server.x509.alternative_names\" | \"tls.server.x509.issuer.common_name\" | \"tls.server.x509.issuer.country\" | \"tls.server.x509.issuer.distinguished_name\" | \"tls.server.x509.issuer.locality\" | \"tls.server.x509.issuer.organization\" | \"tls.server.x509.issuer.organizational_unit\" | \"tls.server.x509.issuer.state_or_province\" | \"tls.server.x509.not_after\" | \"tls.server.x509.not_before\" | \"tls.server.x509.public_key_algorithm\" | \"tls.server.x509.public_key_curve\" | \"tls.server.x509.public_key_exponent\" | \"tls.server.x509.public_key_size\" | \"tls.server.x509.serial_number\" | \"tls.server.x509.signature_algorithm\" | \"tls.server.x509.subject.common_name\" | \"tls.server.x509.subject.country\" | \"tls.server.x509.subject.distinguished_name\" | \"tls.server.x509.subject.locality\" | \"tls.server.x509.subject.organization\" | \"tls.server.x509.subject.organizational_unit\" | \"tls.server.x509.subject.state_or_province\" | \"tls.server.x509.version_number\" | \"tls.version\" | \"tls.version_protocol\" | \"trace.id\" | \"transaction.id\" | \"url.domain\" | \"url.extension\" | \"url.fragment\" | \"url.full\" | \"url.original\" | \"url.password\" | \"url.path\" | \"url.port\" | \"url.query\" | \"url.registered_domain\" | \"url.scheme\" | \"url.subdomain\" | \"url.top_level_domain\" | \"url.username\" | \"user.changes.domain\" | \"user.changes.email\" | \"user.changes.full_name\" | \"user.changes.group.domain\" | \"user.changes.group.id\" | \"user.changes.group.name\" | \"user.changes.hash\" | \"user.changes.id\" | \"user.changes.name\" | \"user.changes.roles\" | \"user.domain\" | \"user.effective.domain\" | \"user.effective.email\" | \"user.effective.full_name\" | \"user.effective.group.domain\" | \"user.effective.group.id\" | \"user.effective.group.name\" | \"user.effective.hash\" | \"user.effective.id\" | \"user.effective.name\" | \"user.effective.roles\" | \"user.email\" | \"user.full_name\" | \"user.group.domain\" | \"user.group.id\" | \"user.group.name\" | \"user.hash\" | \"user.id\" | \"user.name\" | \"user.risk.calculated_level\" | \"user.risk.calculated_score\" | \"user.risk.calculated_score_norm\" | \"user.risk.static_level\" | \"user.risk.static_score\" | \"user.risk.static_score_norm\" | \"user.roles\" | \"user.target.domain\" | \"user.target.email\" | \"user.target.full_name\" | \"user.target.group.domain\" | \"user.target.group.id\" | \"user.target.group.name\" | \"user.target.hash\" | \"user.target.id\" | \"user.target.name\" | \"user.target.roles\" | \"user_agent.device.name\" | \"user_agent.name\" | \"user_agent.original\" | \"user_agent.os.family\" | \"user_agent.os.full\" | \"user_agent.os.kernel\" | \"user_agent.os.name\" | \"user_agent.os.platform\" | \"user_agent.os.type\" | \"user_agent.os.version\" | \"user_agent.version\" | \"vulnerability.category\" | \"vulnerability.classification\" | \"vulnerability.description\" | \"vulnerability.enumeration\" | \"vulnerability.id\" | \"vulnerability.reference\" | \"vulnerability.report_id\" | \"vulnerability.scanner.vendor\" | \"vulnerability.score.base\" | \"vulnerability.score.environmental\" | \"vulnerability.score.temporal\" | \"vulnerability.score.version\" | \"vulnerability.severity\" | \"_id\" | \"_source\" | \"_index\" | \"_ignored\" | \"_routing\" | ", + "\"@timestamp\" | \"event.sequence\" | \"event.start\" | \"event.end\" | \"event.provider\" | \"event.duration\" | \"event.action\" | \"message\" | \"event.outcome\" | \"tags\" | \"event.kind\" | \"event.original\" | \"agent.name\" | \"container.id\" | \"host.name\" | \"labels\" | \"service.environment\" | \"service.name\" | \"ecs.version\" | \"agent.build.original\" | \"agent.ephemeral_id\" | \"agent.id\" | \"agent.type\" | \"agent.version\" | \"client.address\" | \"client.as.number\" | \"client.as.organization.name\" | \"client.bytes\" | \"client.domain\" | \"client.geo.city_name\" | \"client.geo.continent_code\" | \"client.geo.continent_name\" | \"client.geo.country_iso_code\" | \"client.geo.country_name\" | \"client.geo.location\" | \"client.geo.name\" | \"client.geo.postal_code\" | \"client.geo.region_iso_code\" | \"client.geo.region_name\" | \"client.geo.timezone\" | \"client.ip\" | \"client.mac\" | \"client.nat.ip\" | \"client.nat.port\" | \"client.packets\" | \"client.port\" | \"client.registered_domain\" | \"client.subdomain\" | \"client.top_level_domain\" | \"client.user.domain\" | \"client.user.email\" | \"client.user.full_name\" | \"client.user.group.domain\" | \"client.user.group.id\" | \"client.user.group.name\" | \"client.user.hash\" | \"client.user.id\" | \"client.user.name\" | \"client.user.roles\" | \"cloud.account.id\" | \"cloud.account.name\" | \"cloud.availability_zone\" | \"cloud.instance.id\" | \"cloud.instance.name\" | \"cloud.machine.type\" | \"cloud.origin.account.id\" | \"cloud.origin.account.name\" | \"cloud.origin.availability_zone\" | \"cloud.origin.instance.id\" | \"cloud.origin.instance.name\" | \"cloud.origin.machine.type\" | \"cloud.origin.project.id\" | \"cloud.origin.project.name\" | \"cloud.origin.provider\" | \"cloud.origin.region\" | \"cloud.origin.service.name\" | \"cloud.project.id\" | \"cloud.project.name\" | \"cloud.provider\" | \"cloud.region\" | \"cloud.service.name\" | \"cloud.target.account.id\" | \"cloud.target.account.name\" | \"cloud.target.availability_zone\" | \"cloud.target.instance.id\" | \"cloud.target.instance.name\" | \"cloud.target.machine.type\" | \"cloud.target.project.id\" | \"cloud.target.project.name\" | \"cloud.target.provider\" | \"cloud.target.region\" | \"cloud.target.service.name\" | \"container.cpu.usage\" | \"container.disk.read.bytes\" | \"container.disk.write.bytes\" | \"container.image.hash.all\" | \"container.image.name\" | \"container.image.tag\" | \"container.labels\" | \"container.memory.usage\" | \"container.name\" | \"container.network.egress.bytes\" | \"container.network.ingress.bytes\" | \"container.runtime\" | \"container.security_context.privileged\" | \"destination.address\" | \"destination.as.number\" | \"destination.as.organization.name\" | \"destination.bytes\" | \"destination.domain\" | \"destination.geo.city_name\" | \"destination.geo.continent_code\" | \"destination.geo.continent_name\" | \"destination.geo.country_iso_code\" | \"destination.geo.country_name\" | \"destination.geo.location\" | \"destination.geo.name\" | \"destination.geo.postal_code\" | \"destination.geo.region_iso_code\" | \"destination.geo.region_name\" | \"destination.geo.timezone\" | \"destination.ip\" | \"destination.mac\" | \"destination.nat.ip\" | \"destination.nat.port\" | \"destination.packets\" | \"destination.port\" | \"destination.registered_domain\" | \"destination.subdomain\" | \"destination.top_level_domain\" | \"destination.user.domain\" | \"destination.user.email\" | \"destination.user.full_name\" | \"destination.user.group.domain\" | \"destination.user.group.id\" | \"destination.user.group.name\" | \"destination.user.hash\" | \"destination.user.id\" | \"destination.user.name\" | \"destination.user.roles\" | \"device.id\" | \"device.manufacturer\" | \"device.model.identifier\" | \"device.model.name\" | \"dll.code_signature.digest_algorithm\" | \"dll.code_signature.exists\" | \"dll.code_signature.signing_id\" | \"dll.code_signature.status\" | \"dll.code_signature.subject_name\" | \"dll.code_signature.team_id\" | \"dll.code_signature.timestamp\" | \"dll.code_signature.trusted\" | \"dll.code_signature.valid\" | \"dll.hash.md5\" | \"dll.hash.sha1\" | \"dll.hash.sha256\" | \"dll.hash.sha384\" | \"dll.hash.sha512\" | \"dll.hash.ssdeep\" | \"dll.hash.tlsh\" | \"dll.name\" | \"dll.path\" | \"dll.pe.architecture\" | \"dll.pe.company\" | \"dll.pe.description\" | \"dll.pe.file_version\" | \"dll.pe.go_import_hash\" | \"dll.pe.go_imports\" | \"dll.pe.go_imports_names_entropy\" | \"dll.pe.go_imports_names_var_entropy\" | \"dll.pe.go_stripped\" | \"dll.pe.imphash\" | \"dll.pe.import_hash\" | \"dll.pe.imports\" | \"dll.pe.imports_names_entropy\" | \"dll.pe.imports_names_var_entropy\" | \"dll.pe.original_file_name\" | \"dll.pe.pehash\" | \"dll.pe.product\" | \"dll.pe.sections\" | \"dns.answers\" | \"dns.header_flags\" | \"dns.id\" | \"dns.op_code\" | \"dns.question.class\" | \"dns.question.name\" | \"dns.question.registered_domain\" | \"dns.question.subdomain\" | \"dns.question.top_level_domain\" | \"dns.question.type\" | \"dns.resolved_ip\" | \"dns.response_code\" | \"dns.type\" | \"email.attachments\" | \"file.extension\" | \"file.hash.md5\" | \"file.hash.sha1\" | \"file.hash.sha256\" | \"file.hash.sha384\" | \"file.hash.sha512\" | \"file.hash.ssdeep\" | \"file.hash.tlsh\" | \"file.mime_type\" | \"file.name\" | \"file.size\" | \"email.bcc.address\" | \"email.cc.address\" | \"email.content_type\" | \"email.delivery_timestamp\" | \"email.direction\" | \"email.from.address\" | \"email.local_id\" | \"email.message_id\" | \"email.origination_timestamp\" | \"email.reply_to.address\" | \"email.sender.address\" | \"email.subject\" | \"email.to.address\" | \"email.x_mailer\" | \"error.code\" | \"error.id\" | \"error.message\" | \"error.stack_trace\" | \"error.type\" | \"event.agent_id_status\" | \"event.category\" | \"event.code\" | \"event.created\" | \"event.dataset\" | \"event.hash\" | \"event.id\" | \"event.ingested\" | \"event.module\" | \"event.reason\" | \"event.reference\" | \"event.risk_score\" | \"event.risk_score_norm\" | \"event.severity\" | \"event.timezone\" | \"event.type\" | \"event.url\" | \"faas.coldstart\" | \"faas.execution\" | \"faas.id\" | \"faas.name\" | \"faas.version\" | \"file.accessed\" | \"file.attributes\" | \"file.code_signature.digest_algorithm\" | \"file.code_signature.exists\" | \"file.code_signature.signing_id\" | \"file.code_signature.status\" | \"file.code_signature.subject_name\" | \"file.code_signature.team_id\" | \"file.code_signature.timestamp\" | \"file.code_signature.trusted\" | \"file.code_signature.valid\" | \"file.created\" | \"file.ctime\" | \"file.device\" | \"file.directory\" | \"file.drive_letter\" | \"file.elf.architecture\" | \"file.elf.byte_order\" | \"file.elf.cpu_type\" | \"file.elf.creation_date\" | \"file.elf.exports\" | \"file.elf.go_import_hash\" | \"file.elf.go_imports\" | \"file.elf.go_imports_names_entropy\" | \"file.elf.go_imports_names_var_entropy\" | \"file.elf.go_stripped\" | \"file.elf.header.abi_version\" | \"file.elf.header.class\" | \"file.elf.header.data\" | \"file.elf.header.entrypoint\" | \"file.elf.header.object_version\" | \"file.elf.header.os_abi\" | \"file.elf.header.type\" | \"file.elf.header.version\" | \"file.elf.import_hash\" | \"file.elf.imports\" | \"file.elf.imports_names_entropy\" | \"file.elf.imports_names_var_entropy\" | \"file.elf.sections\" | \"file.elf.segments\" | \"file.elf.shared_libraries\" | \"file.elf.telfhash\" | \"file.fork_name\" | \"file.gid\" | \"file.group\" | \"file.inode\" | \"file.macho.go_import_hash\" | \"file.macho.go_imports\" | \"file.macho.go_imports_names_entropy\" | \"file.macho.go_imports_names_var_entropy\" | \"file.macho.go_stripped\" | \"file.macho.import_hash\" | \"file.macho.imports\" | \"file.macho.imports_names_entropy\" | \"file.macho.imports_names_var_entropy\" | \"file.macho.sections\" | \"file.macho.symhash\" | \"file.mode\" | \"file.mtime\" | \"file.owner\" | \"file.path\" | \"file.pe.architecture\" | \"file.pe.company\" | \"file.pe.description\" | \"file.pe.file_version\" | \"file.pe.go_import_hash\" | \"file.pe.go_imports\" | \"file.pe.go_imports_names_entropy\" | \"file.pe.go_imports_names_var_entropy\" | \"file.pe.go_stripped\" | \"file.pe.imphash\" | \"file.pe.import_hash\" | \"file.pe.imports\" | \"file.pe.imports_names_entropy\" | \"file.pe.imports_names_var_entropy\" | \"file.pe.original_file_name\" | \"file.pe.pehash\" | \"file.pe.product\" | \"file.pe.sections\" | \"file.target_path\" | \"file.type\" | \"file.uid\" | \"file.x509.alternative_names\" | \"file.x509.issuer.common_name\" | \"file.x509.issuer.country\" | \"file.x509.issuer.distinguished_name\" | \"file.x509.issuer.locality\" | \"file.x509.issuer.organization\" | \"file.x509.issuer.organizational_unit\" | \"file.x509.issuer.state_or_province\" | \"file.x509.not_after\" | \"file.x509.not_before\" | \"file.x509.public_key_algorithm\" | \"file.x509.public_key_curve\" | \"file.x509.public_key_exponent\" | \"file.x509.public_key_size\" | \"file.x509.serial_number\" | \"file.x509.signature_algorithm\" | \"file.x509.subject.common_name\" | \"file.x509.subject.country\" | \"file.x509.subject.distinguished_name\" | \"file.x509.subject.locality\" | \"file.x509.subject.organization\" | \"file.x509.subject.organizational_unit\" | \"file.x509.subject.state_or_province\" | \"file.x509.version_number\" | \"group.domain\" | \"group.id\" | \"group.name\" | \"host.architecture\" | \"host.boot.id\" | \"host.cpu.usage\" | \"host.disk.read.bytes\" | \"host.disk.write.bytes\" | \"host.domain\" | \"host.geo.city_name\" | \"host.geo.continent_code\" | \"host.geo.continent_name\" | \"host.geo.country_iso_code\" | \"host.geo.country_name\" | \"host.geo.location\" | \"host.geo.name\" | \"host.geo.postal_code\" | \"host.geo.region_iso_code\" | \"host.geo.region_name\" | \"host.geo.timezone\" | \"host.hostname\" | \"host.id\" | \"host.ip\" | \"host.mac\" | \"host.network.egress.bytes\" | \"host.network.egress.packets\" | \"host.network.ingress.bytes\" | \"host.network.ingress.packets\" | \"host.os.family\" | \"host.os.full\" | \"host.os.kernel\" | \"host.os.name\" | \"host.os.platform\" | \"host.os.type\" | \"host.os.version\" | \"host.pid_ns_ino\" | \"host.risk.calculated_level\" | \"host.risk.calculated_score\" | \"host.risk.calculated_score_norm\" | \"host.risk.static_level\" | \"host.risk.static_score\" | \"host.risk.static_score_norm\" | \"host.type\" | \"host.uptime\" | \"http.request.body.bytes\" | \"http.request.body.content\" | \"http.request.bytes\" | \"http.request.id\" | \"http.request.method\" | \"http.request.mime_type\" | \"http.request.referrer\" | \"http.response.body.bytes\" | \"http.response.body.content\" | \"http.response.bytes\" | \"http.response.mime_type\" | \"http.response.status_code\" | \"http.version\" | \"log.file.path\" | \"log.level\" | \"log.logger\" | \"log.origin.file.line\" | \"log.origin.file.name\" | \"log.origin.function\" | \"log.syslog\" | \"network.application\" | \"network.bytes\" | \"network.community_id\" | \"network.direction\" | \"network.forwarded_ip\" | \"network.iana_number\" | \"network.inner\" | \"network.name\" | \"network.packets\" | \"network.protocol\" | \"network.transport\" | \"network.type\" | \"network.vlan.id\" | \"network.vlan.name\" | \"observer.egress\" | \"observer.geo.city_name\" | \"observer.geo.continent_code\" | \"observer.geo.continent_name\" | \"observer.geo.country_iso_code\" | \"observer.geo.country_name\" | \"observer.geo.location\" | \"observer.geo.name\" | \"observer.geo.postal_code\" | \"observer.geo.region_iso_code\" | \"observer.geo.region_name\" | \"observer.geo.timezone\" | \"observer.hostname\" | \"observer.ingress\" | \"observer.ip\" | \"observer.mac\" | \"observer.name\" | \"observer.os.family\" | \"observer.os.full\" | \"observer.os.kernel\" | \"observer.os.name\" | \"observer.os.platform\" | \"observer.os.type\" | \"observer.os.version\" | \"observer.product\" | \"observer.serial_number\" | \"observer.type\" | \"observer.vendor\" | \"observer.version\" | \"orchestrator.api_version\" | \"orchestrator.cluster.id\" | \"orchestrator.cluster.name\" | \"orchestrator.cluster.url\" | \"orchestrator.cluster.version\" | \"orchestrator.namespace\" | \"orchestrator.organization\" | \"orchestrator.resource.annotation\" | \"orchestrator.resource.id\" | \"orchestrator.resource.ip\" | \"orchestrator.resource.label\" | \"orchestrator.resource.name\" | \"orchestrator.resource.parent.type\" | \"orchestrator.resource.type\" | \"orchestrator.type\" | \"organization.id\" | \"organization.name\" | \"package.architecture\" | \"package.build_version\" | \"package.checksum\" | \"package.description\" | \"package.install_scope\" | \"package.installed\" | \"package.license\" | \"package.name\" | \"package.path\" | \"package.reference\" | \"package.size\" | \"package.type\" | \"package.version\" | \"process.args\" | \"process.args_count\" | \"process.code_signature.digest_algorithm\" | \"process.code_signature.exists\" | \"process.code_signature.signing_id\" | \"process.code_signature.status\" | \"process.code_signature.subject_name\" | \"process.code_signature.team_id\" | \"process.code_signature.timestamp\" | \"process.code_signature.trusted\" | \"process.code_signature.valid\" | \"process.command_line\" | \"process.elf.architecture\" | \"process.elf.byte_order\" | \"process.elf.cpu_type\" | \"process.elf.creation_date\" | \"process.elf.exports\" | \"process.elf.go_import_hash\" | \"process.elf.go_imports\" | \"process.elf.go_imports_names_entropy\" | \"process.elf.go_imports_names_var_entropy\" | \"process.elf.go_stripped\" | \"process.elf.header.abi_version\" | \"process.elf.header.class\" | \"process.elf.header.data\" | \"process.elf.header.entrypoint\" | \"process.elf.header.object_version\" | \"process.elf.header.os_abi\" | \"process.elf.header.type\" | \"process.elf.header.version\" | \"process.elf.import_hash\" | \"process.elf.imports\" | \"process.elf.imports_names_entropy\" | \"process.elf.imports_names_var_entropy\" | \"process.elf.sections\" | \"process.elf.segments\" | \"process.elf.shared_libraries\" | \"process.elf.telfhash\" | \"process.end\" | \"process.entity_id\" | \"process.entry_leader.args\" | \"process.entry_leader.args_count\" | \"process.entry_leader.attested_groups.name\" | \"process.entry_leader.attested_user.id\" | \"process.entry_leader.attested_user.name\" | \"process.entry_leader.command_line\" | \"process.entry_leader.entity_id\" | \"process.entry_leader.entry_meta.source.ip\" | \"process.entry_leader.entry_meta.type\" | \"process.entry_leader.executable\" | \"process.entry_leader.group.id\" | \"process.entry_leader.group.name\" | \"process.entry_leader.interactive\" | \"process.entry_leader.name\" | \"process.entry_leader.parent.entity_id\" | \"process.entry_leader.parent.pid\" | \"process.entry_leader.parent.session_leader.entity_id\" | \"process.entry_leader.parent.session_leader.pid\" | \"process.entry_leader.parent.session_leader.start\" | \"process.entry_leader.parent.session_leader.vpid\" | \"process.entry_leader.parent.start\" | \"process.entry_leader.parent.vpid\" | \"process.entry_leader.pid\" | \"process.entry_leader.real_group.id\" | \"process.entry_leader.real_group.name\" | \"process.entry_leader.real_user.id\" | \"process.entry_leader.real_user.name\" | \"process.entry_leader.same_as_process\" | \"process.entry_leader.saved_group.id\" | \"process.entry_leader.saved_group.name\" | \"process.entry_leader.saved_user.id\" | \"process.entry_leader.saved_user.name\" | \"process.entry_leader.start\" | \"process.entry_leader.supplemental_groups.id\" | \"process.entry_leader.supplemental_groups.name\" | \"process.entry_leader.tty\" | \"process.entry_leader.user.id\" | \"process.entry_leader.user.name\" | \"process.entry_leader.vpid\" | \"process.entry_leader.working_directory\" | \"process.env_vars\" | \"process.executable\" | \"process.exit_code\" | \"process.group_leader.args\" | \"process.group_leader.args_count\" | \"process.group_leader.command_line\" | \"process.group_leader.entity_id\" | \"process.group_leader.executable\" | \"process.group_leader.group.id\" | \"process.group_leader.group.name\" | \"process.group_leader.interactive\" | \"process.group_leader.name\" | \"process.group_leader.pid\" | \"process.group_leader.real_group.id\" | \"process.group_leader.real_group.name\" | \"process.group_leader.real_user.id\" | \"process.group_leader.real_user.name\" | \"process.group_leader.same_as_process\" | \"process.group_leader.saved_group.id\" | \"process.group_leader.saved_group.name\" | \"process.group_leader.saved_user.id\" | \"process.group_leader.saved_user.name\" | \"process.group_leader.start\" | \"process.group_leader.supplemental_groups.id\" | \"process.group_leader.supplemental_groups.name\" | \"process.group_leader.tty\" | \"process.group_leader.user.id\" | \"process.group_leader.user.name\" | \"process.group_leader.vpid\" | \"process.group_leader.working_directory\" | \"process.hash.md5\" | \"process.hash.sha1\" | \"process.hash.sha256\" | \"process.hash.sha384\" | \"process.hash.sha512\" | \"process.hash.ssdeep\" | \"process.hash.tlsh\" | \"process.interactive\" | \"process.io\" | \"process.macho.go_import_hash\" | \"process.macho.go_imports\" | \"process.macho.go_imports_names_entropy\" | \"process.macho.go_imports_names_var_entropy\" | \"process.macho.go_stripped\" | \"process.macho.import_hash\" | \"process.macho.imports\" | \"process.macho.imports_names_entropy\" | \"process.macho.imports_names_var_entropy\" | \"process.macho.sections\" | \"process.macho.symhash\" | \"process.name\" | \"process.parent.args\" | \"process.parent.args_count\" | \"process.parent.code_signature.digest_algorithm\" | \"process.parent.code_signature.exists\" | \"process.parent.code_signature.signing_id\" | \"process.parent.code_signature.status\" | \"process.parent.code_signature.subject_name\" | \"process.parent.code_signature.team_id\" | \"process.parent.code_signature.timestamp\" | \"process.parent.code_signature.trusted\" | \"process.parent.code_signature.valid\" | \"process.parent.command_line\" | \"process.parent.elf.architecture\" | \"process.parent.elf.byte_order\" | \"process.parent.elf.cpu_type\" | \"process.parent.elf.creation_date\" | \"process.parent.elf.exports\" | \"process.parent.elf.go_import_hash\" | \"process.parent.elf.go_imports\" | \"process.parent.elf.go_imports_names_entropy\" | \"process.parent.elf.go_imports_names_var_entropy\" | \"process.parent.elf.go_stripped\" | \"process.parent.elf.header.abi_version\" | \"process.parent.elf.header.class\" | \"process.parent.elf.header.data\" | \"process.parent.elf.header.entrypoint\" | \"process.parent.elf.header.object_version\" | \"process.parent.elf.header.os_abi\" | \"process.parent.elf.header.type\" | \"process.parent.elf.header.version\" | \"process.parent.elf.import_hash\" | \"process.parent.elf.imports\" | \"process.parent.elf.imports_names_entropy\" | \"process.parent.elf.imports_names_var_entropy\" | \"process.parent.elf.sections\" | \"process.parent.elf.segments\" | \"process.parent.elf.shared_libraries\" | \"process.parent.elf.telfhash\" | \"process.parent.end\" | \"process.parent.entity_id\" | \"process.parent.executable\" | \"process.parent.exit_code\" | \"process.parent.group.id\" | \"process.parent.group.name\" | \"process.parent.group_leader.entity_id\" | \"process.parent.group_leader.pid\" | \"process.parent.group_leader.start\" | \"process.parent.group_leader.vpid\" | \"process.parent.hash.md5\" | \"process.parent.hash.sha1\" | \"process.parent.hash.sha256\" | \"process.parent.hash.sha384\" | \"process.parent.hash.sha512\" | \"process.parent.hash.ssdeep\" | \"process.parent.hash.tlsh\" | \"process.parent.interactive\" | \"process.parent.macho.go_import_hash\" | \"process.parent.macho.go_imports\" | \"process.parent.macho.go_imports_names_entropy\" | \"process.parent.macho.go_imports_names_var_entropy\" | \"process.parent.macho.go_stripped\" | \"process.parent.macho.import_hash\" | \"process.parent.macho.imports\" | \"process.parent.macho.imports_names_entropy\" | \"process.parent.macho.imports_names_var_entropy\" | \"process.parent.macho.sections\" | \"process.parent.macho.symhash\" | \"process.parent.name\" | \"process.parent.pe.architecture\" | \"process.parent.pe.company\" | \"process.parent.pe.description\" | \"process.parent.pe.file_version\" | \"process.parent.pe.go_import_hash\" | \"process.parent.pe.go_imports\" | \"process.parent.pe.go_imports_names_entropy\" | \"process.parent.pe.go_imports_names_var_entropy\" | \"process.parent.pe.go_stripped\" | \"process.parent.pe.imphash\" | \"process.parent.pe.import_hash\" | \"process.parent.pe.imports\" | \"process.parent.pe.imports_names_entropy\" | \"process.parent.pe.imports_names_var_entropy\" | \"process.parent.pe.original_file_name\" | \"process.parent.pe.pehash\" | \"process.parent.pe.product\" | \"process.parent.pe.sections\" | \"process.parent.pgid\" | \"process.parent.pid\" | \"process.parent.real_group.id\" | \"process.parent.real_group.name\" | \"process.parent.real_user.id\" | \"process.parent.real_user.name\" | \"process.parent.saved_group.id\" | \"process.parent.saved_group.name\" | \"process.parent.saved_user.id\" | \"process.parent.saved_user.name\" | \"process.parent.start\" | \"process.parent.supplemental_groups.id\" | \"process.parent.supplemental_groups.name\" | \"process.parent.thread.capabilities.effective\" | \"process.parent.thread.capabilities.permitted\" | \"process.parent.thread.id\" | \"process.parent.thread.name\" | \"process.parent.title\" | \"process.parent.tty\" | \"process.parent.uptime\" | \"process.parent.user.id\" | \"process.parent.user.name\" | \"process.parent.vpid\" | \"process.parent.working_directory\" | \"process.pe.architecture\" | \"process.pe.company\" | \"process.pe.description\" | \"process.pe.file_version\" | \"process.pe.go_import_hash\" | \"process.pe.go_imports\" | \"process.pe.go_imports_names_entropy\" | \"process.pe.go_imports_names_var_entropy\" | \"process.pe.go_stripped\" | \"process.pe.imphash\" | \"process.pe.import_hash\" | \"process.pe.imports\" | \"process.pe.imports_names_entropy\" | \"process.pe.imports_names_var_entropy\" | \"process.pe.original_file_name\" | \"process.pe.pehash\" | \"process.pe.product\" | \"process.pe.sections\" | \"process.pgid\" | \"process.pid\" | \"process.previous.args\" | \"process.previous.args_count\" | \"process.previous.executable\" | \"process.real_group.id\" | \"process.real_group.name\" | \"process.real_user.id\" | \"process.real_user.name\" | \"process.saved_group.id\" | \"process.saved_group.name\" | \"process.saved_user.id\" | \"process.saved_user.name\" | \"process.session_leader.args\" | \"process.session_leader.args_count\" | \"process.session_leader.command_line\" | \"process.session_leader.entity_id\" | \"process.session_leader.executable\" | \"process.session_leader.group.id\" | \"process.session_leader.group.name\" | \"process.session_leader.interactive\" | \"process.session_leader.name\" | \"process.session_leader.parent.entity_id\" | \"process.session_leader.parent.pid\" | \"process.session_leader.parent.session_leader.entity_id\" | \"process.session_leader.parent.session_leader.pid\" | \"process.session_leader.parent.session_leader.start\" | \"process.session_leader.parent.session_leader.vpid\" | \"process.session_leader.parent.start\" | \"process.session_leader.parent.vpid\" | \"process.session_leader.pid\" | \"process.session_leader.real_group.id\" | \"process.session_leader.real_group.name\" | \"process.session_leader.real_user.id\" | \"process.session_leader.real_user.name\" | \"process.session_leader.same_as_process\" | \"process.session_leader.saved_group.id\" | \"process.session_leader.saved_group.name\" | \"process.session_leader.saved_user.id\" | \"process.session_leader.saved_user.name\" | \"process.session_leader.start\" | \"process.session_leader.supplemental_groups.id\" | \"process.session_leader.supplemental_groups.name\" | \"process.session_leader.tty\" | \"process.session_leader.user.id\" | \"process.session_leader.user.name\" | \"process.session_leader.vpid\" | \"process.session_leader.working_directory\" | \"process.start\" | \"process.supplemental_groups.id\" | \"process.supplemental_groups.name\" | \"process.thread.capabilities.effective\" | \"process.thread.capabilities.permitted\" | \"process.thread.id\" | \"process.thread.name\" | \"process.title\" | \"process.tty\" | \"process.uptime\" | \"process.user.id\" | \"process.user.name\" | \"process.vpid\" | \"process.working_directory\" | \"registry.data.bytes\" | \"registry.data.strings\" | \"registry.data.type\" | \"registry.hive\" | \"registry.key\" | \"registry.path\" | \"registry.value\" | \"related.hash\" | \"related.hosts\" | \"related.ip\" | \"related.user\" | \"rule.author\" | \"rule.category\" | \"rule.description\" | \"rule.id\" | \"rule.license\" | \"rule.name\" | \"rule.reference\" | \"rule.ruleset\" | \"rule.uuid\" | \"rule.version\" | \"server.address\" | \"server.as.number\" | \"server.as.organization.name\" | \"server.bytes\" | \"server.domain\" | \"server.geo.city_name\" | \"server.geo.continent_code\" | \"server.geo.continent_name\" | \"server.geo.country_iso_code\" | \"server.geo.country_name\" | \"server.geo.location\" | \"server.geo.name\" | \"server.geo.postal_code\" | \"server.geo.region_iso_code\" | \"server.geo.region_name\" | \"server.geo.timezone\" | \"server.ip\" | \"server.mac\" | \"server.nat.ip\" | \"server.nat.port\" | \"server.packets\" | \"server.port\" | \"server.registered_domain\" | \"server.subdomain\" | \"server.top_level_domain\" | \"server.user.domain\" | \"server.user.email\" | \"server.user.full_name\" | \"server.user.group.domain\" | \"server.user.group.id\" | \"server.user.group.name\" | \"server.user.hash\" | \"server.user.id\" | \"server.user.name\" | \"server.user.roles\" | \"service.address\" | \"service.ephemeral_id\" | \"service.id\" | \"service.node.name\" | \"service.node.role\" | \"service.node.roles\" | \"service.origin.address\" | \"service.origin.environment\" | \"service.origin.ephemeral_id\" | \"service.origin.id\" | \"service.origin.name\" | \"service.origin.node.name\" | \"service.origin.node.role\" | \"service.origin.node.roles\" | \"service.origin.state\" | \"service.origin.type\" | \"service.origin.version\" | \"service.state\" | \"service.target.address\" | \"service.target.environment\" | \"service.target.ephemeral_id\" | \"service.target.id\" | \"service.target.name\" | \"service.target.node.name\" | \"service.target.node.role\" | \"service.target.node.roles\" | \"service.target.state\" | \"service.target.type\" | \"service.target.version\" | \"service.type\" | \"service.version\" | \"source.address\" | \"source.as.number\" | \"source.as.organization.name\" | \"source.bytes\" | \"source.domain\" | \"source.geo.city_name\" | \"source.geo.continent_code\" | \"source.geo.continent_name\" | \"source.geo.country_iso_code\" | \"source.geo.country_name\" | \"source.geo.location\" | \"source.geo.name\" | \"source.geo.postal_code\" | \"source.geo.region_iso_code\" | \"source.geo.region_name\" | \"source.geo.timezone\" | \"source.ip\" | \"source.mac\" | \"source.nat.ip\" | \"source.nat.port\" | \"source.packets\" | \"source.port\" | \"source.registered_domain\" | \"source.subdomain\" | \"source.top_level_domain\" | \"source.user.domain\" | \"source.user.email\" | \"source.user.full_name\" | \"source.user.group.domain\" | \"source.user.group.id\" | \"source.user.group.name\" | \"source.user.hash\" | \"source.user.id\" | \"source.user.name\" | \"source.user.roles\" | \"span.id\" | \"threat.enrichments\" | \"threat.feed.dashboard_id\" | \"threat.feed.description\" | \"threat.feed.name\" | \"threat.feed.reference\" | \"threat.framework\" | \"threat.group.alias\" | \"threat.group.id\" | \"threat.group.name\" | \"threat.group.reference\" | \"threat.indicator.as.number\" | \"threat.indicator.as.organization.name\" | \"threat.indicator.confidence\" | \"threat.indicator.description\" | \"threat.indicator.email.address\" | \"threat.indicator.file.accessed\" | \"threat.indicator.file.attributes\" | \"threat.indicator.file.code_signature.digest_algorithm\" | \"threat.indicator.file.code_signature.exists\" | \"threat.indicator.file.code_signature.signing_id\" | \"threat.indicator.file.code_signature.status\" | \"threat.indicator.file.code_signature.subject_name\" | \"threat.indicator.file.code_signature.team_id\" | \"threat.indicator.file.code_signature.timestamp\" | \"threat.indicator.file.code_signature.trusted\" | \"threat.indicator.file.code_signature.valid\" | \"threat.indicator.file.created\" | \"threat.indicator.file.ctime\" | \"threat.indicator.file.device\" | \"threat.indicator.file.directory\" | \"threat.indicator.file.drive_letter\" | \"threat.indicator.file.elf.architecture\" | \"threat.indicator.file.elf.byte_order\" | \"threat.indicator.file.elf.cpu_type\" | \"threat.indicator.file.elf.creation_date\" | \"threat.indicator.file.elf.exports\" | \"threat.indicator.file.elf.go_import_hash\" | \"threat.indicator.file.elf.go_imports\" | \"threat.indicator.file.elf.go_imports_names_entropy\" | \"threat.indicator.file.elf.go_imports_names_var_entropy\" | \"threat.indicator.file.elf.go_stripped\" | \"threat.indicator.file.elf.header.abi_version\" | \"threat.indicator.file.elf.header.class\" | \"threat.indicator.file.elf.header.data\" | \"threat.indicator.file.elf.header.entrypoint\" | \"threat.indicator.file.elf.header.object_version\" | \"threat.indicator.file.elf.header.os_abi\" | \"threat.indicator.file.elf.header.type\" | \"threat.indicator.file.elf.header.version\" | \"threat.indicator.file.elf.import_hash\" | \"threat.indicator.file.elf.imports\" | \"threat.indicator.file.elf.imports_names_entropy\" | \"threat.indicator.file.elf.imports_names_var_entropy\" | \"threat.indicator.file.elf.sections\" | \"threat.indicator.file.elf.segments\" | \"threat.indicator.file.elf.shared_libraries\" | \"threat.indicator.file.elf.telfhash\" | \"threat.indicator.file.extension\" | \"threat.indicator.file.fork_name\" | \"threat.indicator.file.gid\" | \"threat.indicator.file.group\" | \"threat.indicator.file.hash.md5\" | \"threat.indicator.file.hash.sha1\" | \"threat.indicator.file.hash.sha256\" | \"threat.indicator.file.hash.sha384\" | \"threat.indicator.file.hash.sha512\" | \"threat.indicator.file.hash.ssdeep\" | \"threat.indicator.file.hash.tlsh\" | \"threat.indicator.file.inode\" | \"threat.indicator.file.mime_type\" | \"threat.indicator.file.mode\" | \"threat.indicator.file.mtime\" | \"threat.indicator.file.name\" | \"threat.indicator.file.owner\" | \"threat.indicator.file.path\" | \"threat.indicator.file.pe.architecture\" | \"threat.indicator.file.pe.company\" | \"threat.indicator.file.pe.description\" | \"threat.indicator.file.pe.file_version\" | \"threat.indicator.file.pe.go_import_hash\" | \"threat.indicator.file.pe.go_imports\" | \"threat.indicator.file.pe.go_imports_names_entropy\" | \"threat.indicator.file.pe.go_imports_names_var_entropy\" | \"threat.indicator.file.pe.go_stripped\" | \"threat.indicator.file.pe.imphash\" | \"threat.indicator.file.pe.import_hash\" | \"threat.indicator.file.pe.imports\" | \"threat.indicator.file.pe.imports_names_entropy\" | \"threat.indicator.file.pe.imports_names_var_entropy\" | \"threat.indicator.file.pe.original_file_name\" | \"threat.indicator.file.pe.pehash\" | \"threat.indicator.file.pe.product\" | \"threat.indicator.file.pe.sections\" | \"threat.indicator.file.size\" | \"threat.indicator.file.target_path\" | \"threat.indicator.file.type\" | \"threat.indicator.file.uid\" | \"threat.indicator.file.x509.alternative_names\" | \"threat.indicator.file.x509.issuer.common_name\" | \"threat.indicator.file.x509.issuer.country\" | \"threat.indicator.file.x509.issuer.distinguished_name\" | \"threat.indicator.file.x509.issuer.locality\" | \"threat.indicator.file.x509.issuer.organization\" | \"threat.indicator.file.x509.issuer.organizational_unit\" | \"threat.indicator.file.x509.issuer.state_or_province\" | \"threat.indicator.file.x509.not_after\" | \"threat.indicator.file.x509.not_before\" | \"threat.indicator.file.x509.public_key_algorithm\" | \"threat.indicator.file.x509.public_key_curve\" | \"threat.indicator.file.x509.public_key_exponent\" | \"threat.indicator.file.x509.public_key_size\" | \"threat.indicator.file.x509.serial_number\" | \"threat.indicator.file.x509.signature_algorithm\" | \"threat.indicator.file.x509.subject.common_name\" | \"threat.indicator.file.x509.subject.country\" | \"threat.indicator.file.x509.subject.distinguished_name\" | \"threat.indicator.file.x509.subject.locality\" | \"threat.indicator.file.x509.subject.organization\" | \"threat.indicator.file.x509.subject.organizational_unit\" | \"threat.indicator.file.x509.subject.state_or_province\" | \"threat.indicator.file.x509.version_number\" | \"threat.indicator.first_seen\" | \"threat.indicator.geo.city_name\" | \"threat.indicator.geo.continent_code\" | \"threat.indicator.geo.continent_name\" | \"threat.indicator.geo.country_iso_code\" | \"threat.indicator.geo.country_name\" | \"threat.indicator.geo.location\" | \"threat.indicator.geo.name\" | \"threat.indicator.geo.postal_code\" | \"threat.indicator.geo.region_iso_code\" | \"threat.indicator.geo.region_name\" | \"threat.indicator.geo.timezone\" | \"threat.indicator.ip\" | \"threat.indicator.last_seen\" | \"threat.indicator.marking.tlp\" | \"threat.indicator.marking.tlp_version\" | \"threat.indicator.modified_at\" | \"threat.indicator.name\" | \"threat.indicator.port\" | \"threat.indicator.provider\" | \"threat.indicator.reference\" | \"threat.indicator.registry.data.bytes\" | \"threat.indicator.registry.data.strings\" | \"threat.indicator.registry.data.type\" | \"threat.indicator.registry.hive\" | \"threat.indicator.registry.key\" | \"threat.indicator.registry.path\" | \"threat.indicator.registry.value\" | \"threat.indicator.scanner_stats\" | \"threat.indicator.sightings\" | \"threat.indicator.type\" | \"threat.indicator.url.domain\" | \"threat.indicator.url.extension\" | \"threat.indicator.url.fragment\" | \"threat.indicator.url.full\" | \"threat.indicator.url.original\" | \"threat.indicator.url.password\" | \"threat.indicator.url.path\" | \"threat.indicator.url.port\" | \"threat.indicator.url.query\" | \"threat.indicator.url.registered_domain\" | \"threat.indicator.url.scheme\" | \"threat.indicator.url.subdomain\" | \"threat.indicator.url.top_level_domain\" | \"threat.indicator.url.username\" | \"threat.indicator.x509.alternative_names\" | \"threat.indicator.x509.issuer.common_name\" | \"threat.indicator.x509.issuer.country\" | \"threat.indicator.x509.issuer.distinguished_name\" | \"threat.indicator.x509.issuer.locality\" | \"threat.indicator.x509.issuer.organization\" | \"threat.indicator.x509.issuer.organizational_unit\" | \"threat.indicator.x509.issuer.state_or_province\" | \"threat.indicator.x509.not_after\" | \"threat.indicator.x509.not_before\" | \"threat.indicator.x509.public_key_algorithm\" | \"threat.indicator.x509.public_key_curve\" | \"threat.indicator.x509.public_key_exponent\" | \"threat.indicator.x509.public_key_size\" | \"threat.indicator.x509.serial_number\" | \"threat.indicator.x509.signature_algorithm\" | \"threat.indicator.x509.subject.common_name\" | \"threat.indicator.x509.subject.country\" | \"threat.indicator.x509.subject.distinguished_name\" | \"threat.indicator.x509.subject.locality\" | \"threat.indicator.x509.subject.organization\" | \"threat.indicator.x509.subject.organizational_unit\" | \"threat.indicator.x509.subject.state_or_province\" | \"threat.indicator.x509.version_number\" | \"threat.software.alias\" | \"threat.software.id\" | \"threat.software.name\" | \"threat.software.platforms\" | \"threat.software.reference\" | \"threat.software.type\" | \"threat.tactic.id\" | \"threat.tactic.name\" | \"threat.tactic.reference\" | \"threat.technique.id\" | \"threat.technique.name\" | \"threat.technique.reference\" | \"threat.technique.subtechnique.id\" | \"threat.technique.subtechnique.name\" | \"threat.technique.subtechnique.reference\" | \"tls.cipher\" | \"tls.client.certificate\" | \"tls.client.certificate_chain\" | \"tls.client.hash.md5\" | \"tls.client.hash.sha1\" | \"tls.client.hash.sha256\" | \"tls.client.issuer\" | \"tls.client.ja3\" | \"tls.client.not_after\" | \"tls.client.not_before\" | \"tls.client.server_name\" | \"tls.client.subject\" | \"tls.client.supported_ciphers\" | \"tls.client.x509.alternative_names\" | \"tls.client.x509.issuer.common_name\" | \"tls.client.x509.issuer.country\" | \"tls.client.x509.issuer.distinguished_name\" | \"tls.client.x509.issuer.locality\" | \"tls.client.x509.issuer.organization\" | \"tls.client.x509.issuer.organizational_unit\" | \"tls.client.x509.issuer.state_or_province\" | \"tls.client.x509.not_after\" | \"tls.client.x509.not_before\" | \"tls.client.x509.public_key_algorithm\" | \"tls.client.x509.public_key_curve\" | \"tls.client.x509.public_key_exponent\" | \"tls.client.x509.public_key_size\" | \"tls.client.x509.serial_number\" | \"tls.client.x509.signature_algorithm\" | \"tls.client.x509.subject.common_name\" | \"tls.client.x509.subject.country\" | \"tls.client.x509.subject.distinguished_name\" | \"tls.client.x509.subject.locality\" | \"tls.client.x509.subject.organization\" | \"tls.client.x509.subject.organizational_unit\" | \"tls.client.x509.subject.state_or_province\" | \"tls.client.x509.version_number\" | \"tls.curve\" | \"tls.established\" | \"tls.next_protocol\" | \"tls.resumed\" | \"tls.server.certificate\" | \"tls.server.certificate_chain\" | \"tls.server.hash.md5\" | \"tls.server.hash.sha1\" | \"tls.server.hash.sha256\" | \"tls.server.issuer\" | \"tls.server.ja3s\" | \"tls.server.not_after\" | \"tls.server.not_before\" | \"tls.server.subject\" | \"tls.server.x509.alternative_names\" | \"tls.server.x509.issuer.common_name\" | \"tls.server.x509.issuer.country\" | \"tls.server.x509.issuer.distinguished_name\" | \"tls.server.x509.issuer.locality\" | \"tls.server.x509.issuer.organization\" | \"tls.server.x509.issuer.organizational_unit\" | \"tls.server.x509.issuer.state_or_province\" | \"tls.server.x509.not_after\" | \"tls.server.x509.not_before\" | \"tls.server.x509.public_key_algorithm\" | \"tls.server.x509.public_key_curve\" | \"tls.server.x509.public_key_exponent\" | \"tls.server.x509.public_key_size\" | \"tls.server.x509.serial_number\" | \"tls.server.x509.signature_algorithm\" | \"tls.server.x509.subject.common_name\" | \"tls.server.x509.subject.country\" | \"tls.server.x509.subject.distinguished_name\" | \"tls.server.x509.subject.locality\" | \"tls.server.x509.subject.organization\" | \"tls.server.x509.subject.organizational_unit\" | \"tls.server.x509.subject.state_or_province\" | \"tls.server.x509.version_number\" | \"tls.version\" | \"tls.version_protocol\" | \"trace.id\" | \"transaction.id\" | \"url.domain\" | \"url.extension\" | \"url.fragment\" | \"url.full\" | \"url.original\" | \"url.password\" | \"url.path\" | \"url.port\" | \"url.query\" | \"url.registered_domain\" | \"url.scheme\" | \"url.subdomain\" | \"url.top_level_domain\" | \"url.username\" | \"user.changes.domain\" | \"user.changes.email\" | \"user.changes.full_name\" | \"user.changes.group.domain\" | \"user.changes.group.id\" | \"user.changes.group.name\" | \"user.changes.hash\" | \"user.changes.id\" | \"user.changes.name\" | \"user.changes.roles\" | \"user.domain\" | \"user.effective.domain\" | \"user.effective.email\" | \"user.effective.full_name\" | \"user.effective.group.domain\" | \"user.effective.group.id\" | \"user.effective.group.name\" | \"user.effective.hash\" | \"user.effective.id\" | \"user.effective.name\" | \"user.effective.roles\" | \"user.email\" | \"user.full_name\" | \"user.group.domain\" | \"user.group.id\" | \"user.group.name\" | \"user.hash\" | \"user.id\" | \"user.name\" | \"user.risk.calculated_level\" | \"user.risk.calculated_score\" | \"user.risk.calculated_score_norm\" | \"user.risk.static_level\" | \"user.risk.static_score\" | \"user.risk.static_score_norm\" | \"user.roles\" | \"user.target.domain\" | \"user.target.email\" | \"user.target.full_name\" | \"user.target.group.domain\" | \"user.target.group.id\" | \"user.target.group.name\" | \"user.target.hash\" | \"user.target.id\" | \"user.target.name\" | \"user.target.roles\" | \"user_agent.device.name\" | \"user_agent.name\" | \"user_agent.original\" | \"user_agent.os.family\" | \"user_agent.os.full\" | \"user_agent.os.kernel\" | \"user_agent.os.name\" | \"user_agent.os.platform\" | \"user_agent.os.type\" | \"user_agent.os.version\" | \"user_agent.version\" | \"vulnerability.category\" | \"vulnerability.classification\" | \"vulnerability.description\" | \"vulnerability.enumeration\" | \"vulnerability.id\" | \"vulnerability.reference\" | \"vulnerability.report_id\" | \"vulnerability.scanner.vendor\" | \"vulnerability.score.base\" | \"vulnerability.score.environmental\" | \"vulnerability.score.temporal\" | \"vulnerability.score.version\" | \"vulnerability.severity\" | \"_id\" | \"_source\" | \"_index\" | \"_routing\" | \"_ignored\" | ", { "pluginId": "fieldsMetadata", "scope": "common", diff --git a/api_docs/fields_metadata.mdx b/api_docs/fields_metadata.mdx index 5834d3568074b..25cbf24dd6aba 100644 --- a/api_docs/fields_metadata.mdx +++ b/api_docs/fields_metadata.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldsMetadata title: "fieldsMetadata" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldsMetadata plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldsMetadata'] --- import fieldsMetadataObj from './fields_metadata.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 3a176ccc800e4..0d7404c49413e 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 5c28be306f7c2..07c7ea0447888 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-08-06 +date: 2024-08-09 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 dca28e2bcdd63..ad392100e992b 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-08-06 +date: 2024-08-09 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 8743a2c493c80..54f1b16567154 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -8089,13 +8089,7 @@ "description": [], "signature": [ "(options: { pkgName: string; pkgVersion?: string | undefined; spaceId?: string | undefined; force?: boolean | undefined; }) => Promise<", - { - "pluginId": "fleet", - "scope": "common", - "docId": "kibFleetPluginApi", - "section": "def-common.Installation", - "text": "Installation" - }, + "EnsurePackageResult", ">" ], "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", @@ -23223,7 +23217,7 @@ "label": "monitoring_enabled", "description": [], "signature": [ - "(\"metrics\" | \"logs\")[] | undefined" + "(\"metrics\" | \"traces\" | \"logs\")[] | undefined" ], "path": "x-pack/plugins/fleet/common/types/models/agent_policy.ts", "deprecated": false, @@ -27646,7 +27640,7 @@ "label": "PackageSpecCategory", "description": [], "signature": [ - "\"monitoring\" | \"security\" | \"connector\" | \"observability\" | \"infrastructure\" | \"cloud\" | \"custom\" | \"enterprise_search\" | \"advanced_analytics_ueba\" | \"analytics_engine\" | \"application_observability\" | \"app_search\" | \"auditd\" | \"authentication\" | \"aws\" | \"azure\" | \"big_data\" | \"cdn_security\" | \"config_management\" | \"connector_client\" | \"containers\" | \"crawler\" | \"credential_management\" | \"crm\" | \"custom_logs\" | \"database_security\" | \"datastore\" | \"dns_security\" | \"edr_xdr\" | \"cloudsecurity_cdr\" | \"elasticsearch_sdk\" | \"elastic_stack\" | \"email_security\" | \"firewall_security\" | \"google_cloud\" | \"iam\" | \"ids_ips\" | \"java_observability\" | \"kubernetes\" | \"language_client\" | \"languages\" | \"load_balancer\" | \"message_queue\" | \"native_search\" | \"network\" | \"network_security\" | \"notification\" | \"os_system\" | \"process_manager\" | \"productivity\" | \"productivity_security\" | \"proxy_security\" | \"sdk_search\" | \"stream_processing\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"virtualization\" | \"vpn_security\" | \"vulnerability_management\" | \"web\" | \"web_application_firewall\" | \"websphere\" | \"workplace_search_content_source\" | \"workplace_search\"" + "\"monitoring\" | \"security\" | \"connector\" | \"observability\" | \"custom\" | \"infrastructure\" | \"cloud\" | \"enterprise_search\" | \"advanced_analytics_ueba\" | \"analytics_engine\" | \"application_observability\" | \"app_search\" | \"auditd\" | \"authentication\" | \"aws\" | \"azure\" | \"big_data\" | \"cdn_security\" | \"config_management\" | \"connector_client\" | \"containers\" | \"crawler\" | \"credential_management\" | \"crm\" | \"custom_logs\" | \"database_security\" | \"datastore\" | \"dns_security\" | \"edr_xdr\" | \"cloudsecurity_cdr\" | \"elasticsearch_sdk\" | \"elastic_stack\" | \"email_security\" | \"firewall_security\" | \"google_cloud\" | \"iam\" | \"ids_ips\" | \"java_observability\" | \"kubernetes\" | \"language_client\" | \"languages\" | \"load_balancer\" | \"message_queue\" | \"native_search\" | \"network\" | \"network_security\" | \"notification\" | \"os_system\" | \"process_manager\" | \"productivity\" | \"productivity_security\" | \"proxy_security\" | \"sdk_search\" | \"stream_processing\" | \"support\" | \"threat_intel\" | \"ticketing\" | \"version_control\" | \"virtualization\" | \"vpn_security\" | \"vulnerability_management\" | \"web\" | \"web_application_firewall\" | \"websphere\" | \"workplace_search_content_source\" | \"workplace_search\"" ], "path": "x-pack/plugins/fleet/common/types/models/package_spec.ts", "deprecated": false, @@ -27768,7 +27762,7 @@ "label": "RegistrySearchResult", "description": [], "signature": [ - "{ type?: \"input\" | \"integration\" | undefined; version: string; name: string; title: string; description: string; path: string; download: string; internal?: boolean | undefined; icons?: (", + "{ type?: \"input\" | \"integration\" | undefined; version: string; name: string; title: string; description: string; path: string; internal?: boolean | undefined; download: string; icons?: (", { "pluginId": "fleet", "scope": "common", diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 0ce13046d5cb1..319503b068c82 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-08-06 +date: 2024-08-09 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 | |-------------------|-----------|------------------------|-----------------| -| 1351 | 5 | 1229 | 73 | +| 1351 | 5 | 1229 | 74 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index c338ef9a9ef85..4747686e0c57d 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-08-06 +date: 2024-08-09 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 9086c8a5e6e12..ffd302c8cc856 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-08-06 +date: 2024-08-09 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 ba0db8db110fa..f7d1f75abebf1 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-08-06 +date: 2024-08-09 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 c5e016817ce77..3e435e146860b 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-08-06 +date: 2024-08-09 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 7b215db37fee2..fe5657db2892a 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-08-06 +date: 2024-08-09 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 47db80e4e6368..27c7391fd2d78 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/inference.devdocs.json b/api_docs/inference.devdocs.json new file mode 100644 index 0000000000000..642be486024c6 --- /dev/null +++ b/api_docs/inference.devdocs.json @@ -0,0 +1,318 @@ +{ + "id": "inference", + "client": { + "classes": [], + "functions": [ + { + "parentPluginId": "inference", + "id": "def-public.httpResponseIntoObservable", + "type": "Function", + "tags": [], + "label": "httpResponseIntoObservable", + "description": [], + "signature": [ + "() => ", + "OperatorFunction", + "<", + "StreamedHttpResponse", + ", T>" + ], + "path": "x-pack/plugins/inference/public/util/http_response_into_observable.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "inference", + "id": "def-public.InferencePublicSetup", + "type": "Interface", + "tags": [], + "label": "InferencePublicSetup", + "description": [], + "path": "x-pack/plugins/inference/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "inference", + "id": "def-public.InferencePublicStart", + "type": "Interface", + "tags": [], + "label": "InferencePublicStart", + "description": [], + "path": "x-pack/plugins/inference/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "inference", + "id": "def-public.InferencePublicStart.chatComplete", + "type": "Function", + "tags": [], + "label": "chatComplete", + "description": [], + "signature": [ + "(options: { connectorId: string; system?: string | undefined; messages: ", + "Message", + "[]; } & ", + "ToolOptions", + ") => ", + "ChatCompletionResponse", + "<", + "ToolOptions", + ">" + ], + "path": "x-pack/plugins/inference/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "inference", + "id": "def-public.InferencePublicStart.chatComplete.$1", + "type": "CompoundType", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "{ connectorId: string; system?: string | undefined; messages: ", + "Message", + "[]; } & TToolOptions" + ], + "path": "x-pack/plugins/inference/common/chat_complete/index.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "inference", + "id": "def-public.InferencePublicStart.output", + "type": "Function", + "tags": [], + "label": "output", + "description": [], + "signature": [ + "(id: TId, options: { connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; }) => ", + "Observable", + "<", + "OutputEvent", + " : undefined>>" + ], + "path": "x-pack/plugins/inference/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "inference", + "id": "def-public.InferencePublicStart.output.$1", + "type": "Uncategorized", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "TId" + ], + "path": "x-pack/plugins/inference/common/output/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "inference", + "id": "def-public.InferencePublicStart.output.$2", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "{ connectorId: string; system?: string | undefined; input: string; schema?: TOutputSchema | undefined; }" + ], + "path": "x-pack/plugins/inference/common/output/index.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "inference", + "id": "def-public.InferencePublicStart.getConnectors", + "type": "Function", + "tags": [], + "label": "getConnectors", + "description": [], + "signature": [ + "() => Promise<", + "InferenceConnector", + "[]>" + ], + "path": "x-pack/plugins/inference/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "server": { + "classes": [], + "functions": [ + { + "parentPluginId": "inference", + "id": "def-server.withoutChunkEvents", + "type": "Function", + "tags": [], + "label": "withoutChunkEvents", + "description": [], + "signature": [ + "() => ", + "OperatorFunction", + ">" + ], + "path": "x-pack/plugins/inference/common/chat_complete/without_chunk_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "inference", + "id": "def-server.withoutOutputUpdateEvents", + "type": "Function", + "tags": [], + "label": "withoutOutputUpdateEvents", + "description": [], + "signature": [ + "() => ", + "OperatorFunction", + ">>" + ], + "path": "x-pack/plugins/inference/common/output/without_output_update_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "inference", + "id": "def-server.withoutTokenCountEvents", + "type": "Function", + "tags": [], + "label": "withoutTokenCountEvents", + "description": [], + "signature": [ + "() => ", + "OperatorFunction", + ">" + ], + "path": "x-pack/plugins/inference/common/chat_complete/without_token_count_events.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [], + "setup": { + "parentPluginId": "inference", + "id": "def-server.InferenceServerSetup", + "type": "Interface", + "tags": [], + "label": "InferenceServerSetup", + "description": [], + "path": "x-pack/plugins/inference/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "inference", + "id": "def-server.InferenceServerStart", + "type": "Interface", + "tags": [], + "label": "InferenceServerStart", + "description": [], + "path": "x-pack/plugins/inference/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "inference", + "id": "def-server.InferenceServerStart.getClient", + "type": "Function", + "tags": [], + "label": "getClient", + "description": [ + "\nCreates an inference client, scoped to a request.\n" + ], + "signature": [ + "(options: InferenceClientCreateOptions) => ", + "InferenceClient" + ], + "path": "x-pack/plugins/inference/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "inference", + "id": "def-server.InferenceServerStart.getClient.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [ + "{@link InferenceClientCreateOptions }" + ], + "signature": [ + "InferenceClientCreateOptions" + ], + "path": "x-pack/plugins/inference/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/inference.mdx b/api_docs/inference.mdx new file mode 100644 index 0000000000000..5533a277640a2 --- /dev/null +++ b/api_docs/inference.mdx @@ -0,0 +1,47 @@ +--- +#### +#### 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: kibInferencePluginApi +slug: /kibana-dev-docs/api/inference +title: "inference" +image: https://source.unsplash.com/400x175/?github +description: API docs for the inference plugin +date: 2024-08-09 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inference'] +--- +import inferenceObj from './inference.devdocs.json'; + + + +Contact [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 16 | 0 | 14 | 11 | + +## Client + +### Setup + + +### Start + + +### Functions + + +## Server + +### Setup + + +### Start + + +### Functions + + diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 38c3cdc774c07..8e8c2532078b1 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index f1e8f7655e649..9029485c0c6f2 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 06a3e9a27e59a..f74be9ae581f3 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/integration_assistant.mdx b/api_docs/integration_assistant.mdx index 5c277c72612d8..ec8c9e146bfc5 100644 --- a/api_docs/integration_assistant.mdx +++ b/api_docs/integration_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/integrationAssistant title: "integrationAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the integrationAssistant plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'integrationAssistant'] --- import integrationAssistantObj from './integration_assistant.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index c53ebe0407817..e17e0e6bf5b23 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/investigate.devdocs.json b/api_docs/investigate.devdocs.json index 7048808862f2d..84d3ffbb38d41 100644 --- a/api_docs/investigate.devdocs.json +++ b/api_docs/investigate.devdocs.json @@ -145,7 +145,7 @@ "label": "getEsFilterFromGlobalParameters", "description": [], "signature": [ - "({\n filters,\n timeRange,\n}: Partial<", + "({ timeRange }: Partial<", "GlobalWidgetParameters", ">) => { bool: ", { @@ -166,7 +166,7 @@ "id": "def-public.getEsFilterFromGlobalParameters.$1", "type": "Object", "tags": [], - "label": "{\n filters,\n timeRange,\n}", + "label": "{ timeRange }", "description": [], "signature": [ "Partial<", @@ -487,27 +487,6 @@ "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.GlobalWidgetParameters.filters", - "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/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -681,17 +660,6 @@ "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.InvestigateWidget.locked", - "type": "boolean", - "tags": [], - "label": "locked", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -749,27 +717,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "investigate", - "id": "def-public.Investigation.revisions", - "type": "Array", - "tags": [], - "label": "revisions", - "description": [], - "signature": [ - { - "pluginId": "investigate", - "scope": "common", - "docId": "kibInvestigatePluginApi", - "section": "def-common.InvestigationRevision", - "text": "InvestigationRevision" - }, - "[]" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "investigate", "id": "def-public.Investigation.title", @@ -783,43 +730,7 @@ }, { "parentPluginId": "investigate", - "id": "def-public.Investigation.revision", - "type": "string", - "tags": [], - "label": "revision", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.InvestigationRevision", - "type": "Interface", - "tags": [], - "label": "InvestigationRevision", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "investigate", - "id": "def-public.InvestigationRevision.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.InvestigationRevision.items", + "id": "def-public.Investigation.items", "type": "Array", "tags": [], "label": "items", @@ -840,7 +751,7 @@ }, { "parentPluginId": "investigate", - "id": "def-public.InvestigationRevision.parameters", + "id": "def-public.Investigation.parameters", "type": "Object", "tags": [], "label": "parameters", @@ -921,7 +832,7 @@ "section": "def-common.InvestigateWidget", "text": "InvestigateWidget" }, - "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\" | \"locked\"> & { parameters: ", + "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\"> & { parameters: ", "_DeepPartialObject", "<", "GlobalWidgetParameters", @@ -932,136 +843,6 @@ "trackAdoption": false } ] - }, - { - "parentPluginId": "investigate", - "id": "def-public.WidgetRenderAPI.blocks", - "type": "Object", - "tags": [], - "label": "blocks", - "description": [], - "signature": [ - "{ publish: (blocks: ", - { - "pluginId": "investigate", - "scope": "common", - "docId": "kibInvestigatePluginApi", - "section": "def-common.WorkflowBlock", - "text": "WorkflowBlock" - }, - "[]) => UnregisterFunction; }" - ], - "path": "x-pack/plugins/observability_solution/investigate/public/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock", - "type": "Interface", - "tags": [], - "label": "WorkflowBlock", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock.content", - "type": "string", - "tags": [], - "label": "content", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock.loading", - "type": "boolean", - "tags": [], - "label": "loading", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock.onClick", - "type": "Function", - "tags": [], - "label": "onClick", - "description": [], - "signature": [ - "(() => void) | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock.color", - "type": "CompoundType", - "tags": [], - "label": "color", - "description": [], - "signature": [ - "\"link\" | \"text\" | \"title\" | \"warning\" | \"disabled\" | \"success\" | \"body\" | \"highlight\" | \"primary\" | \"accent\" | \"danger\" | \"primaryText\" | \"accentText\" | \"successText\" | \"warningText\" | \"dangerText\" | \"emptyShade\" | \"lightestShade\" | \"lightShade\" | \"mediumShade\" | \"darkShade\" | \"darkestShade\" | \"fullShade\" | \"disabledText\" | \"shadow\" | \"subduedText\" | \"ghost\" | \"ink\" | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-public.WorkflowBlock.children", - "type": "CompoundType", - "tags": [], - "label": "children", - "description": [], - "signature": [ - "boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -1125,7 +906,7 @@ "section": "def-common.InvestigateWidget", "text": "InvestigateWidget" }, - "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\" | \"locked\"> & { parameters: ", + "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\"> & { parameters: ", "_DeepPartialObject", "<", "GlobalWidgetParameters", @@ -1175,7 +956,7 @@ "section": "def-common.InvestigateWidget", "text": "InvestigateWidget" }, - "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\" | \"locked\"> & { parameters: ", + "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\"> & { parameters: ", "_DeepPartialObject", "<", "GlobalWidgetParameters", @@ -1824,17 +1605,6 @@ "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.InvestigateWidget.locked", - "type": "boolean", - "tags": [], - "label": "locked", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -1892,27 +1662,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "investigate", - "id": "def-common.Investigation.revisions", - "type": "Array", - "tags": [], - "label": "revisions", - "description": [], - "signature": [ - { - "pluginId": "investigate", - "scope": "common", - "docId": "kibInvestigatePluginApi", - "section": "def-common.InvestigationRevision", - "text": "InvestigationRevision" - }, - "[]" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "investigate", "id": "def-common.Investigation.title", @@ -1926,43 +1675,7 @@ }, { "parentPluginId": "investigate", - "id": "def-common.Investigation.revision", - "type": "string", - "tags": [], - "label": "revision", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.InvestigationRevision", - "type": "Interface", - "tags": [], - "label": "InvestigationRevision", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "investigate", - "id": "def-common.InvestigationRevision.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.InvestigationRevision.items", + "id": "def-common.Investigation.items", "type": "Array", "tags": [], "label": "items", @@ -1983,7 +1696,7 @@ }, { "parentPluginId": "investigate", - "id": "def-common.InvestigationRevision.parameters", + "id": "def-common.Investigation.parameters", "type": "Object", "tags": [], "label": "parameters", @@ -1997,114 +1710,6 @@ } ], "initialIsOpen": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock", - "type": "Interface", - "tags": [], - "label": "WorkflowBlock", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock.id", - "type": "string", - "tags": [], - "label": "id", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock.content", - "type": "string", - "tags": [], - "label": "content", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock.description", - "type": "string", - "tags": [], - "label": "description", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock.loading", - "type": "boolean", - "tags": [], - "label": "loading", - "description": [], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock.onClick", - "type": "Function", - "tags": [], - "label": "onClick", - "description": [], - "signature": [ - "(() => void) | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - }, - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock.color", - "type": "CompoundType", - "tags": [], - "label": "color", - "description": [], - "signature": [ - "\"link\" | \"text\" | \"title\" | \"warning\" | \"disabled\" | \"success\" | \"body\" | \"highlight\" | \"primary\" | \"accent\" | \"danger\" | \"primaryText\" | \"accentText\" | \"successText\" | \"warningText\" | \"dangerText\" | \"emptyShade\" | \"lightestShade\" | \"lightShade\" | \"mediumShade\" | \"darkShade\" | \"darkestShade\" | \"fullShade\" | \"disabledText\" | \"shadow\" | \"subduedText\" | \"ghost\" | \"ink\" | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "investigate", - "id": "def-common.WorkflowBlock.children", - "type": "CompoundType", - "tags": [], - "label": "children", - "description": [], - "signature": [ - "boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined" - ], - "path": "x-pack/plugins/observability_solution/investigate/common/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false } ], "enums": [ @@ -2138,7 +1743,7 @@ "section": "def-common.InvestigateWidget", "text": "InvestigateWidget" }, - "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\" | \"locked\"> & { parameters: ", + "<{}, {}>, \"type\" | \"title\" | \"columns\" | \"description\" | \"rows\"> & { parameters: ", "_DeepPartialObject", "<", "GlobalWidgetParameters", diff --git a/api_docs/investigate.mdx b/api_docs/investigate.mdx index 97179dcd0de3a..717c6f28cb803 100644 --- a/api_docs/investigate.mdx +++ b/api_docs/investigate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigate title: "investigate" image: https://source.unsplash.com/400x175/?github description: API docs for the investigate plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigate'] --- import investigateObj from './investigate.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 | |-------------------|-----------|------------------------|-----------------| -| 133 | 0 | 133 | 6 | +| 105 | 0 | 105 | 6 | ## Client diff --git a/api_docs/investigate_app.devdocs.json b/api_docs/investigate_app.devdocs.json index 7c5650360609d..d8b91a87450ee 100644 --- a/api_docs/investigate_app.devdocs.json +++ b/api_docs/investigate_app.devdocs.json @@ -50,7 +50,49 @@ "label": "InvestigateAppServerRouteRepository", "description": [], "signature": [ - "{}" + "{ \"GET /api/observability/investigations/{id} 2023-10-31\": { endpoint: \"GET /api/observability/investigations/{id} 2023-10-31\"; params?: ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; }> | undefined; handler: ({}: ", + "InvestigateAppRouteHandlerResources", + " & { params: { path: { id: string; }; }; }) => Promise<{ id: string; title: string; createdAt: number; createdBy: string; parameters: { timeRange: { from: number; to: number; }; }; }>; } & ", + "InvestigateAppRouteCreateOptions", + "; \"GET /api/observability/investigations 2023-10-31\": { endpoint: \"GET /api/observability/investigations 2023-10-31\"; params?: ", + "PartialC", + "<{ query: ", + "PartialC", + "<{ page: ", + "StringC", + "; perPage: ", + "StringC", + "; }>; }> | undefined; handler: ({}: ", + "InvestigateAppRouteHandlerResources", + " & { params?: { query?: { page?: string | undefined; perPage?: string | undefined; } | undefined; } | undefined; }) => Promise<{ page: number; perPage: number; total: number; results: { id: string; title: string; createdAt: number; createdBy: string; parameters: { timeRange: { from: number; to: number; }; }; }[]; }>; } & ", + "InvestigateAppRouteCreateOptions", + "; \"POST /api/observability/investigations 2023-10-31\": { endpoint: \"POST /api/observability/investigations 2023-10-31\"; params?: ", + "TypeC", + "<{ body: ", + "TypeC", + "<{ id: ", + "StringC", + "; title: ", + "StringC", + "; parameters: ", + "TypeC", + "<{ timeRange: ", + "TypeC", + "<{ from: ", + "NumberC", + "; to: ", + "NumberC", + "; }>; }>; }>; }> | undefined; handler: ({}: ", + "InvestigateAppRouteHandlerResources", + " & { params: { body: { id: string; title: string; parameters: { timeRange: { from: number; to: number; }; }; }; }; }) => Promise<{ id: string; title: string; createdAt: number; createdBy: string; parameters: { timeRange: { from: number; to: number; }; }; }>; } & ", + "InvestigateAppRouteCreateOptions", + "; }" ], "path": "x-pack/plugins/observability_solution/investigate_app/server/routes/get_global_investigate_app_server_route_repository.ts", "deprecated": false, diff --git a/api_docs/investigate_app.mdx b/api_docs/investigate_app.mdx index c633b0a23a85c..283cc524dab53 100644 --- a/api_docs/investigate_app.mdx +++ b/api_docs/investigate_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigateApp title: "investigateApp" image: https://source.unsplash.com/400x175/?github description: API docs for the investigateApp plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigateApp'] --- import investigateAppObj from './investigate_app.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 | |-------------------|-----------|------------------------|-----------------| -| 5 | 0 | 5 | 0 | +| 5 | 0 | 5 | 2 | ## Client diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index fe6b59128b31d..5b09e6be47824 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2024-08-06 +date: 2024-08-09 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 22eb1f680b38f..8ee9a22546086 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-08-06 +date: 2024-08-09 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 a8d86f45ff4d2..75de3d4ecf3b4 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index 830cb6f4d58bf..b9be6ee6094fb 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index 42e5f6488031e..e02b4abff3319 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index ecc369c452ffe..7765adb08b69c 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_comparators.mdx b/api_docs/kbn_alerting_comparators.mdx index 6ba67629a90be..b3b2c1e95d790 100644 --- a/api_docs/kbn_alerting_comparators.mdx +++ b/api_docs/kbn_alerting_comparators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-comparators title: "@kbn/alerting-comparators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-comparators plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-comparators'] --- import kbnAlertingComparatorsObj from './kbn_alerting_comparators.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index a04e0658525aa..f9c6b696406c8 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-08-06 +date: 2024-08-09 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 922010fd9c710..c2ce13f320024 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index e90c887d1b328..31637fbc455df 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_grouping.mdx b/api_docs/kbn_alerts_grouping.mdx index eca6951ea498a..c27c6790f0d32 100644 --- a/api_docs/kbn_alerts_grouping.mdx +++ b/api_docs/kbn_alerts_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-grouping title: "@kbn/alerts-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-grouping plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-grouping'] --- import kbnAlertsGroupingObj from './kbn_alerts_grouping.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.devdocs.json b/api_docs/kbn_alerts_ui_shared.devdocs.json index b5c33068763e4..17e09808e3c68 100644 --- a/api_docs/kbn_alerts_ui_shared.devdocs.json +++ b/api_docs/kbn_alerts_ui_shared.devdocs.json @@ -2827,7 +2827,15 @@ "section": "def-common.DataViewLazy", "text": "DataViewLazy" }, - ">; createDataViewLazy: (spec: ", + ">; getDataViewLazyFromCache: (id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>; createDataViewLazy: (spec: ", { "pluginId": "dataViews", "scope": "common", @@ -4746,7 +4754,15 @@ "section": "def-common.DataViewLazy", "text": "DataViewLazy" }, - ">; createDataViewLazy: (spec: ", + ">; getDataViewLazyFromCache: (id: string) => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataViewLazy", + "text": "DataViewLazy" + }, + " | undefined>; createDataViewLazy: (spec: ", { "pluginId": "dataViews", "scope": "common", diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 6068793ca127d..ac998c5bb4316 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-08-06 +date: 2024-08-09 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 4cdc19655e05f..dd5f22a14f394 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index f402d32d7cdec..06b57d303d835 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 564e63b8b1cb6..f4b71ba43faf9 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index d7d53d3cdfe92..9f46de34ea606 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index d5374eb001278..26f74245bed6a 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index ffea09af744a6..d8f456992fd82 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_types.mdx b/api_docs/kbn_apm_types.mdx index 37492afa845e2..ae6b2b8e752b7 100644 --- a/api_docs/kbn_apm_types.mdx +++ b/api_docs/kbn_apm_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-types title: "@kbn/apm-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-types plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-types'] --- import kbnApmTypesObj from './kbn_apm_types.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 5ccfdb59e34b0..d89f300c22cae 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_avc_banner.mdx b/api_docs/kbn_avc_banner.mdx index cb180dabfeb0f..c5162e3b2766b 100644 --- a/api_docs/kbn_avc_banner.mdx +++ b/api_docs/kbn_avc_banner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-avc-banner title: "@kbn/avc-banner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/avc-banner plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/avc-banner'] --- import kbnAvcBannerObj from './kbn_avc_banner.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 95afbcfca429b..1dc2f49f4c87d 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index 36fba0b982ea8..86b7dd66f9f3a 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 7a4237e14cb4b..e85729810262a 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-08-06 +date: 2024-08-09 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 e73f2a86d712a..9e1a7a1cb093e 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-08-06 +date: 2024-08-09 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 f69cfda298c28..7e82a0e7fee71 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-08-06 +date: 2024-08-09 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 eb2450423e24a..9379e60e8b414 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-08-06 +date: 2024-08-09 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 e6a792aa778ea..e242e6aae319d 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-08-06 +date: 2024-08-09 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 99b2b86ab438e..8d3a5251e12ed 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-08-06 +date: 2024-08-09 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 109591b4db0f4..e8c4ee961a525 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-08-06 +date: 2024-08-09 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 4a464716b3abd..265b03ec36d10 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-08-06 +date: 2024-08-09 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 96255876b384d..48f5eb050e190 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-08-06 +date: 2024-08-09 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 7f7165d02e193..d3501c233f8f2 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-08-06 +date: 2024-08-09 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 f706625d8f3db..0bcf2cae47ec7 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 93fb5d60278f6..07ff53da835cc 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index 2affe65d34c2a..693e8e420adc5 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index cdea82550a783..34a2479160975 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index c1ecc4be18900..9bece4544b547 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.devdocs.json b/api_docs/kbn_config_mocks.devdocs.json index 922f5422a35c3..bebb572bec47a 100644 --- a/api_docs/kbn_config_mocks.devdocs.json +++ b/api_docs/kbn_config_mocks.devdocs.json @@ -352,7 +352,7 @@ "section": "def-server.ConfigPath", "text": "ConfigPath" }, - ", dynamicConfigPaths: string[]], unknown>; setDynamicConfigOverrides: jest.MockInstance], unknown>; } & ", + ", dynamicConfigPaths: string[]], unknown>; setDynamicConfigOverrides: jest.MockInstance, [newOverrides: Record], unknown>; } & ", "IConfigService" ], "path": "packages/kbn-config-mocks/src/config_service.mock.ts", diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index c6043a8ffec15..5d02a6662e46b 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 3477f84a5bb48..ba1bc5a16484f 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-08-06 +date: 2024-08-09 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 b945e2dc9ae26..f9125cd04200f 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-08-06 +date: 2024-08-09 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 3f5f4468d1e76..e1c3bf569facc 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-08-06 +date: 2024-08-09 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 4e23702548384..ca81004357a49 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-08-06 +date: 2024-08-09 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 3bd8914d64bbc..8b9e12ab76b71 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-08-06 +date: 2024-08-09 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 c15f5e16139ae..1af17d5f3154c 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_user_profiles.mdx b/api_docs/kbn_content_management_user_profiles.mdx index fee29c580ff42..740064b6d3a3c 100644 --- a/api_docs/kbn_content_management_user_profiles.mdx +++ b/api_docs/kbn_content_management_user_profiles.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-user-profiles title: "@kbn/content-management-user-profiles" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-user-profiles plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-user-profiles'] --- import kbnContentManagementUserProfilesObj from './kbn_content_management_user_profiles.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 131894e5588c5..cb9a33bc508c2 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.devdocs.json b/api_docs/kbn_core_analytics_browser.devdocs.json index 93fb88d726608..502a1f60df079 100644 --- a/api_docs/kbn_core_analytics_browser.devdocs.json +++ b/api_docs/kbn_core_analytics_browser.devdocs.json @@ -934,6 +934,10 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts" @@ -1018,6 +1022,30 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" @@ -1294,6 +1322,30 @@ "plugin": "@kbn/core-analytics-browser-internal", "path": "packages/core/analytics/core-analytics-browser-internal/src/track_clicks.test.ts" }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-internal", "path": "packages/core/analytics/core-analytics-browser-internal/src/track_viewport_size.test.ts" diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 5b7e1b066cc2c..be77f4a5dc67e 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 a2e5bb53baf00..417924b10ef52 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 37b2cd575cc52..edf5580284f34 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.devdocs.json b/api_docs/kbn_core_analytics_server.devdocs.json index 66392709bde01..1194fae25d085 100644 --- a/api_docs/kbn_core_analytics_server.devdocs.json +++ b/api_docs/kbn_core_analytics_server.devdocs.json @@ -942,6 +942,10 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts" @@ -1026,6 +1030,30 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.test.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" @@ -1302,6 +1330,30 @@ "plugin": "@kbn/core-analytics-browser-internal", "path": "packages/core/analytics/core-analytics-browser-internal/src/track_clicks.test.ts" }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, + { + "plugin": "@kbn/core-analytics-browser-internal", + "path": "packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts" + }, { "plugin": "@kbn/core-analytics-browser-internal", "path": "packages/core/analytics/core-analytics-browser-internal/src/track_viewport_size.test.ts" diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 1d24710890f9a..5b21a04dfed79 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-08-06 +date: 2024-08-09 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 3b2dcab964435..694d474e59d20 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 68988210e0033..44eca42f3cec1 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 d1dab44cfdcf9..1806c49376abe 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 b983f2ba9a902..f4d7dcda7a43b 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 03deca179ab07..deedcc6c0d1d0 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 df0dc027ddf1c..ebf6b330ac5e8 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-08-06 +date: 2024-08-09 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 e9365c0f8d24b..f6cb98d24ce2f 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 7e8548272384a..2cb1339ec4852 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 ed4752716c7f2..44906fd3b5426 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 868adebbdf23b..8fa197bdb21d6 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 f7f347caf56d7..91fe59f472191 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-08-06 +date: 2024-08-09 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 b809c43308f9a..2f3ca370b7d34 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 fb8fda99681cc..1e402b4f7ca35 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 e80c73fb2b89a..73c417a283f68 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 9d33974dde1a1..a7cce8af91e49 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-08-06 +date: 2024-08-09 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 1b908ac19ed94..35c000de8c6bb 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-08-06 +date: 2024-08-09 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 e9f2717f1431d..5a6cd6201af6e 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 df9b15d433306..0d1542869897b 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 b57514e745db7..9ed530dd87d31 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 1c3593605a869..02fb5a6f0fd76 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 92d6494dc9f31..eb937c07f7d1f 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 5febd99b77f07..67cbbaee132b0 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 3a31adbbaf656..cde594d8473b3 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 654b72754b0d1..8e874bd2a7d2c 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-08-06 +date: 2024-08-09 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 1f985202113cc..19242bdb3056f 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-08-06 +date: 2024-08-09 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 1b2420a3f7c0e..6a9e89d5f2ef4 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 d1b414366e338..6685dbcf23743 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 d035c83c3ec0e..4a62a2199b5f3 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 617c4a7696801..9a08814a9b235 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 ff7b8454b2334..70091ec03bcd9 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 259fddc1e83fa..42b2c34a1f8b2 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-08-06 +date: 2024-08-09 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 b8e517c5b1a6d..d84cb2989609e 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-08-06 +date: 2024-08-09 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 97d98473b4d78..56442283192c1 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 d049e41615d50..c0867ea7b8610 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 47333de0b8712..e9cd41d0d5c40 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 2a469bc1012d3..358275e28d45d 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 8445edf1a1755..9dc87fad52004 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-08-06 +date: 2024-08-09 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 a9b1a205d1666..174e63c9b804d 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 6f072a58fdafc..fb7388f5262bb 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 0e73f9e56530f..c02cbbf8bc750 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 2f063b0b6c429..8056eb99e729b 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-08-06 +date: 2024-08-09 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 8941827470f2f..38e91b749b11d 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 1fb7f2375328d..55d01d5c2b356 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 b9cd16b700927..b16ebc474e4d4 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 ef1996e1477bb..624aa6ff68159 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 5bbbda7b642ad..ad785a27e9ceb 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 2d4baa7bc4549..3a4906e37b662 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 c20203e9a02b7..d5e1f39451dc9 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 6522c9e300982..de2f5b89041ca 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-08-06 +date: 2024-08-09 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 1957c2239e1f0..087190766d81a 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-08-06 +date: 2024-08-09 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 462139af13a1a..ec026df38c6c6 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 39285899f7499..d08c66510fff3 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 512802cf82f56..7380da51b0c24 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 4dcd21c2a2b1f..bf3c4c90ac97d 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 7b55cc199b270..0cbdb9fb0b75c 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 8009887f51cbb..6cfd0aec342e7 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 47e4809f437c2..6d994379497b4 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 5ea7526010990..f906eb3896b19 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-08-06 +date: 2024-08-09 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 70bad77a4c798..56080c5e812bb 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 d2bd37f7d91bf..d0d08b34cc5a5 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-08-06 +date: 2024-08-09 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 9a78335530b35..b3c361e10c315 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-08-06 +date: 2024-08-09 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 5f268706202d9..2e352e7395919 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 f8e57f9c0dcfd..a0bbfc4eea42a 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 b1d0907edeb98..7dae0f3e92ae3 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 92264e60b4bfe..93485ff3b0444 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 b0c95a63e76e3..cc5185b0ceb81 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -4434,6 +4434,10 @@ "plugin": "indexLifecycleManagement", "path": "x-pack/plugins/index_lifecycle_management/server/routes/api/snapshot_repositories/register_fetch_route.ts" }, + { + "plugin": "inference", + "path": "x-pack/plugins/inference/server/routes/connectors.ts" + }, { "plugin": "ingestPipelines", "path": "x-pack/plugins/ingest_pipelines/server/routes/api/get.ts" @@ -6268,6 +6272,10 @@ "plugin": "security", "path": "x-pack/plugins/security/server/routes/authentication/saml.ts" }, + { + "plugin": "security", + "path": "x-pack/plugins/security/server/routes/authorization/roles/post.ts" + }, { "plugin": "security", "path": "x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts" @@ -7028,6 +7036,10 @@ "plugin": "indexLifecycleManagement", "path": "x-pack/plugins/index_lifecycle_management/server/routes/api/templates/register_add_policy_route.ts" }, + { + "plugin": "inference", + "path": "x-pack/plugins/inference/server/routes/chat_complete.ts" + }, { "plugin": "ingestPipelines", "path": "x-pack/plugins/ingest_pipelines/server/routes/api/create.ts" @@ -8260,6 +8272,10 @@ "plugin": "security", "path": "x-pack/plugins/security/server/routes/role_mapping/post.test.ts" }, + { + "plugin": "security", + "path": "x-pack/plugins/security/server/routes/authorization/roles/post.test.ts" + }, { "plugin": "security", "path": "x-pack/plugins/security/server/routes/authentication/common.test.ts" @@ -16248,6 +16264,10 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions_legacy/api/create_legacy_notification/route.ts" diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index b78b4ecacb793..5100b7f6afe7e 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-08-06 +date: 2024-08-09 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 4d765baf98cd2..e9f72b71cd4e9 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 8cddcaeca261a..aec8780d5f567 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 dac9cfbfe92e6..3090ef4347a9c 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 219258706fd94..9f54f3c78437d 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 fb0b5568677ac..f4011152fb41a 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-08-06 +date: 2024-08-09 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 027ad317298f3..bd59601807d38 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 9a3048eb113e5..1c0f0e4a11757 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 39ae5123c8853..beec46f6d9db7 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 43e79ad7a15be..34c750f82bac9 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 8d6bedec1bf02..93b96e2a645ae 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 f014547a8bdb1..12efcd4466520 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 d6c4d46eb8300..a9d42cd47ce23 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 439d789add7b9..68d6ac1ca7830 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-08-06 +date: 2024-08-09 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 467d41d5adceb..143580bc6bb5e 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 2932b0e522e4e..d67322bf9169f 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 691f36f51521c..bd258ff9948d1 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 418fba222810d..d06dbb23e85f5 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-08-06 +date: 2024-08-09 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 e83ab20d1f84b..170e63282cd8d 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 6ec00f0d04ac9..d15fd56fbe2ff 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 71b0e186cf570..117e13dca012c 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 7b779714ee009..a077383f13d9e 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 299cbed4579a6..a75af415bf139 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-08-06 +date: 2024-08-09 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 71ecc6e1a8fa1..ac0676cd0b463 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 230d4439e7dc3..9533acd8b0fe3 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 48674b487174c..f2b414389fd7e 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 1d43969b2ff2e..f675db73a70f5 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-08-06 +date: 2024-08-09 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 f37a91acd95b3..676313414850f 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 5938a0e156ed5..f9437b5fd84ac 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 4351fa1b16f0c..bf7092c865e84 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 5d225c9471e7e..ca453d071079f 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 1f95b9557cebc..6e19b4f0d4b4f 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 ef332d17cd6fe..8480719363f8d 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 a3fc8426728c9..885f74784d328 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 23b686679642f..566d02babb61f 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 f4681493128b6..84d93e3eaed5a 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 0c53bd6c0f0ce..ca5a33cc85a46 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 adf2bff8834fc..14996c99c56c9 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 56dff2c6ec507..42e84d2b593cc 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-08-06 +date: 2024-08-09 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 b0fff91b9aaae..a29446c980250 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-08-06 +date: 2024-08-09 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 357727fee1246..f353b4316af76 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 55639a7d13728..2a491bc483b76 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-08-06 +date: 2024-08-09 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 b3079ee4654be..8e0809bf9d4f8 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 cb7143b7ecf1b..fa30028828579 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 d6cc4d7409db3..c9ec6e24cfe05 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 b3b4d7c393feb..91eaa30902f3e 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 6b5d40f7d9f4b..788888e4705a8 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 89c9fe7c3de92..057e32e796580 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 5659274fca087..1b9f8f6ede201 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-08-06 +date: 2024-08-09 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 c9b3f7d17862e..13eb6e4585eb3 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 871811b333784..b40d89c5805e9 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 ea6bdb1fbd51b..0f423bd78c27e 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 174b22d5a2589..d9ab12711b469 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 41d89eae08683..9d7d773764ecf 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 5ff99c67ccef5..7534fc5e42ff7 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 023a0aae7dd58..b105b0ed59f7f 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-08-06 +date: 2024-08-09 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 ba770e74caf07..f483ffc5bc952 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 3ed7d5eb8c1b5..4796a221ce2ca 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 d4c59c22224fe..a2c400a196027 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 d9076bab8129f..7e9ac13a350e8 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 1cb60b57f8272..909861f3f3d53 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 3acbf3d8dd5d6..2de5ab39e507d 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 df82d00b023d8..03135f16191d5 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 8b2e12a790069..a8a41e7c727e7 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 577fd44926636..cd5448cd6c19b 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index 88b5dccc79d3c..9624a4545c8c3 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 22066c43715cc..0402917897e9d 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index cfb1c930e2904..f989c3653cf96 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index 63d50472a1199..1593dcc57cfef 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index ca09ba37d9ae7..15aff9bf7e908 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index f8029243bf7b5..4720e05d9fb5b 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 64659ef44dd18..7568bb887dfeb 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-08-06 +date: 2024-08-09 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 b8bc6c43e8eaf..da2502ed4f79d 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 4d1a2ca774fa6..8d14377960f06 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-08-06 +date: 2024-08-09 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 a9ae50d3b6e68..0257be469caf1 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 813538a70a221..b499a1e42024d 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 c39da59bf5403..2774961a3c812 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-08-06 +date: 2024-08-09 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 05ab3cfc96dc7..f02dc28bfce2f 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 416155b99cf04..1a958e1b88f6b 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-08-06 +date: 2024-08-09 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 fcfb1a488608d..cc3456ffeb069 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-08-06 +date: 2024-08-09 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 a0c37a0d78fd2..c6e0fdd0a122d 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-08-06 +date: 2024-08-09 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 ff479e26b743a..3a493a3bd91ba 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 88c62fbfacb16..dc42e82de6fcf 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 37b0060115d19..cdf5d6aa49334 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 dd0291b4dd1f4..2496f91642325 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 35d2a53691c50..00846f0ffbf0d 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 8d0ddff7336fc..2f7d8e487e01b 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 c080be7407f03..f665161d18a7c 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-08-06 +date: 2024-08-09 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 a3c8408b3425d..fdca230ef07d7 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-08-06 +date: 2024-08-09 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 a0e2dddaaccb9..351f975848df3 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 92adfc814f4b6..f8917ecfe6ba7 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 932e6acb5d12e..32480f65b4c63 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-08-06 +date: 2024-08-09 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 9d05b224d08af..4bcdc0a453e8e 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 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 1274c22734d90..fca33731984ea 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index a5fb5cd7d6077..bd3d0381e7df4 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 4faf0a43d1b98..aedf107337bc2 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index 94800cc67b7d2..53a7365df6238 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index b4d54ca0d6eca..b7806d7b6ec4c 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index e928c9aa6c375..8094fe623e0cf 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 4021cc740ff7c..7300993993998 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 9fd0b445f7bee..ba4ecb5a02dbc 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index df624576c7bab..54d3eb0d54009 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 52b7bd768d70a..d93fd4fd7b53c 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 56f71c06600ec..d1d732932864e 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-08-06 +date: 2024-08-09 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 5e247fe04dde1..a9a7cae320917 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-08-06 +date: 2024-08-09 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 0c4a9af06189d..b176dc921daeb 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-08-06 +date: 2024-08-09 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 2ee55eb308815..093955fc39a25 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-08-06 +date: 2024-08-09 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 f72ef2354fbff..af970848e6950 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index deb5dd42a3059..06b4a832bf2b3 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 2f0da565d749a..9270660671311 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index abd9ebf70dabb..43426e12a901b 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index f43ad086ce0d3..34a836301ac67 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 87654da2e0913..de5e5ad065480 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-08-06 +date: 2024-08-09 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 87c363603a368..bdd39e12deff4 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-08-06 +date: 2024-08-09 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 290a600b52410..cecde54dd1efd 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 00beb07bdeb8d..f1117eaa22603 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 974c436ab6569..4218c00f11fd0 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-08-06 +date: 2024-08-09 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 f9e8cc60b104a..ee4019966ca86 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index ec16f07f6669b..5653d7c4d8895 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 1387aa28b8fb9..0fa2bef3adca7 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index 3588274e19f7a..c8a8d2f21feee 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index 7d62468cfbb17..b8f82f64f24c4 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index c2e63093990bc..fff4d2a8a794f 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-08-06 +date: 2024-08-09 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 f85d45b688a07..6158a308c9724 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-08-06 +date: 2024-08-09 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 dfe4948c00f38..05a338844517e 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-08-06 +date: 2024-08-09 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 9c624ff6d55e3..813469ddf6a55 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-08-06 +date: 2024-08-09 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 1c72af80b9066..7c5deb366ed80 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-08-06 +date: 2024-08-09 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 85946f1f6b94e..b44c90bdc7519 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-08-06 +date: 2024-08-09 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 b9adfc0ef762a..81427e7c085ae 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-08-06 +date: 2024-08-09 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 31cc61ad1d039..07b97ab693338 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 d9709fe237a83..7b5e2b7481274 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 00fe888e3e4c7..115ea4a3be083 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-08-06 +date: 2024-08-09 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 7265c72d75810..0d0dca3d62f85 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 fc09327f57c15..3d525619ff8a7 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-08-06 +date: 2024-08-09 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 e15e8ba2f81c4..709e84ec7865d 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index b0e47fea0672a..f92317cf0f791 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 19a91cf30a999..d8c6b738cf088 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index c9288cf795ed8..b4d11b8b1d160 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index f7f5456f7bc59..7c1e63fb179d5 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index a889fd9cb1f37..c956702fbde14 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.devdocs.json b/api_docs/kbn_es.devdocs.json index 6fad581f61b92..96bcb65c45764 100644 --- a/api_docs/kbn_es.devdocs.json +++ b/api_docs/kbn_es.devdocs.json @@ -942,6 +942,18 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/es", + "id": "def-common.STATEFUL_ROLES_ROOT_PATH", + "type": "string", + "tags": [], + "label": "STATEFUL_ROLES_ROOT_PATH", + "description": [], + "path": "packages/kbn-es/src/paths.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/es", "id": "def-common.SYSTEM_INDICES_SUPERUSER", diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 0721b93dcb812..f82ac454c0687 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.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 | |-------------------|-----------|------------------------|-----------------| -| 54 | 0 | 39 | 7 | +| 55 | 0 | 40 | 7 | ## Common diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 0013dd8dcf9d1..bea5acd36cb5b 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-08-06 +date: 2024-08-09 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 1732017959d9a..dbddfce2bc2c8 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 4b2c6f15e0c26..2598a5aa8ea82 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index e6b52fc08f1ea..84215f9aa5dc6 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-08-06 +date: 2024-08-09 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 b345a0105f677..c91cffff4aa89 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.devdocs.json b/api_docs/kbn_esql_ast.devdocs.json index a58895e11ef34..b4dd177a110e4 100644 --- a/api_docs/kbn_esql_ast.devdocs.json +++ b/api_docs/kbn_esql_ast.devdocs.json @@ -377,6 +377,254 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.find", + "type": "Function", + "tags": [], + "label": "find", + "description": [ + "\nFinds and returns the first node that matches the search criteria.\n" + ], + "signature": [ + "(node: ", + "WalkerAstNode", + ", predicate: (node: ", + "ESQLProperNode", + ") => boolean) => ", + "ESQLProperNode", + " | undefined" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.find.$1", + "type": "CompoundType", + "tags": [], + "label": "node", + "description": [ + "AST node to start the search from." + ], + "signature": [ + "WalkerAstNode" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.find.$2", + "type": "Function", + "tags": [], + "label": "predicate", + "description": [ + "A function that returns true if the node matches the search criteria." + ], + "signature": [ + "(node: ", + "ESQLProperNode", + ") => boolean" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "The first node that matches the search criteria." + ] + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.findAll", + "type": "Function", + "tags": [], + "label": "findAll", + "description": [ + "\nFinds and returns all nodes that match the search criteria.\n" + ], + "signature": [ + "(node: ", + "WalkerAstNode", + ", predicate: (node: ", + "ESQLProperNode", + ") => boolean) => ", + "ESQLProperNode", + "[]" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.findAll.$1", + "type": "CompoundType", + "tags": [], + "label": "node", + "description": [ + "AST node to start the search from." + ], + "signature": [ + "WalkerAstNode" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.findAll.$2", + "type": "Function", + "tags": [], + "label": "predicate", + "description": [ + "A function that returns true if the node matches the search criteria." + ], + "signature": [ + "(node: ", + "ESQLProperNode", + ") => boolean" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "All nodes that match the search criteria." + ] + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.match", + "type": "Function", + "tags": [], + "label": "match", + "description": [ + "\nMatches a single node against a template object. Returns the first node\nthat matches the template.\n" + ], + "signature": [ + "(node: ", + "WalkerAstNode", + ", template: ", + "NodeMatchTemplate", + ") => ", + "ESQLProperNode", + " | undefined" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.match.$1", + "type": "CompoundType", + "tags": [], + "label": "node", + "description": [ + "AST node to match against the template." + ], + "signature": [ + "WalkerAstNode" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.match.$2", + "type": "Object", + "tags": [], + "label": "template", + "description": [ + "Template object to match against the node." + ], + "signature": [ + "NodeMatchTemplate" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "The first node that matches the template" + ] + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.matchAll", + "type": "Function", + "tags": [], + "label": "matchAll", + "description": [ + "\nMatches all nodes against a template object. Returns all nodes that match\nthe template.\n" + ], + "signature": [ + "(node: ", + "WalkerAstNode", + ", template: ", + "NodeMatchTemplate", + ") => ", + "ESQLProperNode", + "[]" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.matchAll.$1", + "type": "CompoundType", + "tags": [], + "label": "node", + "description": [ + "AST node to match against the template." + ], + "signature": [ + "WalkerAstNode" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.Walker.matchAll.$2", + "type": "Object", + "tags": [], + "label": "template", + "description": [ + "Template object to match against the node." + ], + "signature": [ + "NodeMatchTemplate" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "All nodes that match the template" + ] + }, { "parentPluginId": "@kbn/esql-ast", "id": "def-common.Walker.findFunction", @@ -2555,6 +2803,44 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.WalkerOptions.visitAny", + "type": "Function", + "tags": [], + "label": "visitAny", + "description": [ + "\nCalled for any node type that does not have a specific visitor.\n" + ], + "signature": [ + "((node: ", + "ESQLProperNode", + ") => void) | undefined" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-ast", + "id": "def-common.WalkerOptions.visitAny.$1", + "type": "CompoundType", + "tags": [], + "label": "node", + "description": [ + "Any valid AST node." + ], + "signature": [ + "ESQLProperNode" + ], + "path": "packages/kbn-esql-ast/src/walker/walker.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index 02b992fd436ea..201fa9f458ed0 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 135 | 1 | 120 | 13 | +| 149 | 1 | 120 | 15 | ## Common diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 1e0b73a5c3efe..80bfa62b3bad8 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.devdocs.json b/api_docs/kbn_esql_validation_autocomplete.devdocs.json index b5149b448e605..9c4af05a18969 100644 --- a/api_docs/kbn_esql_validation_autocomplete.devdocs.json +++ b/api_docs/kbn_esql_validation_autocomplete.devdocs.json @@ -3188,10 +3188,13 @@ { "parentPluginId": "@kbn/esql-validation-autocomplete", "id": "def-common.ESQLRealField.type", - "type": "string", + "type": "CompoundType", "tags": [], "label": "type", "description": [], + "signature": [ + "\"boolean\" | \"geo_point\" | \"geo_shape\" | \"ip\" | \"keyword\" | \"text\" | \"date\" | \"version\" | \"integer\" | \"long\" | \"double\" | \"unsigned_long\" | \"cartesian_point\" | \"cartesian_shape\" | \"counter_integer\" | \"counter_long\" | \"counter_double\" | \"unsupported\"" + ], "path": "packages/kbn-esql-validation-autocomplete/src/validation/types.ts", "deprecated": false, "trackAdoption": false @@ -3204,7 +3207,7 @@ "label": "metadata", "description": [], "signature": [ - "{ description?: string | undefined; type?: string | undefined; } | undefined" + "{ description?: string | undefined; } | undefined" ], "path": "packages/kbn-esql-validation-autocomplete/src/validation/types.ts", "deprecated": false, diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 099dabb5d16b9..4f283efcc130b 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index f816c7b7eabcd..1b5f813e453db 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-08-06 +date: 2024-08-09 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 8d1e1dce7e824..c0765b814e931 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-08-06 +date: 2024-08-09 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 ea5064b066a46..ed44a5ff7102e 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-08-06 +date: 2024-08-09 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 e9fcb1e224c4b..d59599b42e91e 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-08-06 +date: 2024-08-09 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 68b1a6fd94c6c..d157494cb0126 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 b9bd3359e9c2d..b1054e0196c11 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index d719680894ac3..b75017126c961 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.devdocs.json b/api_docs/kbn_ftr_common_functional_services.devdocs.json index 8b01b5e5f2d67..9c00accd18000 100644 --- a/api_docs/kbn_ftr_common_functional_services.devdocs.json +++ b/api_docs/kbn_ftr_common_functional_services.devdocs.json @@ -434,7 +434,64 @@ } ], "functions": [], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.RoleCredentials", + "type": "Interface", + "tags": [], + "label": "RoleCredentials", + "description": [], + "path": "packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.RoleCredentials.apiKey", + "type": "Object", + "tags": [], + "label": "apiKey", + "description": [], + "signature": [ + "{ id: string; name: string; }" + ], + "path": "packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.RoleCredentials.apiKeyHeader", + "type": "Object", + "tags": [], + "label": "apiKeyHeader", + "description": [], + "signature": [ + "{ Authorization: string; }" + ], + "path": "packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.RoleCredentials.cookieHeader", + "type": "Object", + "tags": [], + "label": "cookieHeader", + "description": [], + "signature": [ + "{ Cookie: string; }" + ], + "path": "packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], "enums": [], "misc": [ { @@ -541,7 +598,49 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", { "pluginId": "@kbn/ftr-common-functional-services", "scope": "common", @@ -603,13 +702,70 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }>, ProvidedTypeMap<{}>>" + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }>, ProvidedTypeMap<{}>>" ], "path": "packages/kbn-ftr-common-functional-services/services/ftr_provider_context.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.InternalRequestHeader", + "type": "Type", + "tags": [], + "label": "InternalRequestHeader", + "description": [], + "signature": [ + "{ 'kbn-xsrf': string; } | { 'x-elastic-internal-origin': string; 'kbn-xsrf': string; }" + ], + "path": "packages/kbn-ftr-common-functional-services/services/saml_auth/default_request_headers.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/ftr-common-functional-services", "id": "def-common.KibanaServer", @@ -763,7 +919,49 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", { "pluginId": "@kbn/ftr-common-functional-services", "scope": "common", @@ -825,7 +1023,49 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }>, ProvidedTypeMap<{}>>" + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }>, ProvidedTypeMap<{}>>" ], "path": "packages/kbn-ftr-common-functional-services/services/es.ts", "deprecated": false, @@ -940,7 +1180,49 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", { "pluginId": "@kbn/ftr-common-functional-services", "scope": "common", @@ -1002,7 +1284,49 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }>, ProvidedTypeMap<{}>>" + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }>, ProvidedTypeMap<{}>>" ], "path": "packages/kbn-ftr-common-functional-services/services/kibana_server/kibana_server.ts", "deprecated": false, @@ -1117,7 +1441,49 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", { "pluginId": "@kbn/ftr-common-functional-services", "scope": "common", @@ -1179,21 +1545,63 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }>, ProvidedTypeMap<{}>>" - ], - "path": "packages/kbn-ftr-common-functional-services/services/es_archiver.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/ftr-common-functional-services", - "id": "def-common.services.retry", - "type": "Object", - "tags": [], - "label": "retry", - "description": [], + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }>, ProvidedTypeMap<{}>>" + ], + "path": "packages/kbn-ftr-common-functional-services/services/es_archiver.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.services.retry", + "type": "Object", + "tags": [], + "label": "retry", + "description": [], "signature": [ "typeof ", { @@ -1312,7 +1720,49 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", { "pluginId": "@kbn/ftr-common-functional-services", "scope": "common", @@ -1374,13 +1824,343 @@ "node_modules/@types/supertest/lib/agent", "<", "SuperTestStatic", - ".Test>; }>, ProvidedTypeMap<{}>>" + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }>, ProvidedTypeMap<{}>>" ], "path": "packages/kbn-ftr-common-functional-services/services/supertest_without_auth.ts", "deprecated": false, "trackAdoption": false } ] + }, + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.services.samlAuth", + "type": "Function", + "tags": [], + "label": "samlAuth", + "description": [], + "signature": [ + "({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>" + ], + "path": "packages/kbn-ftr-common-functional-services/services/all.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/ftr-common-functional-services", + "id": "def-common.services.samlAuth.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + { + "pluginId": "@kbn/test", + "scope": "common", + "docId": "kibKbnTestPluginApi", + "section": "def-common.GenericFtrProviderContext", + "text": "GenericFtrProviderContext" + }, + "<{ es: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + "default", + "; kibanaServer: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + { + "pluginId": "@kbn/test", + "scope": "common", + "docId": "kibKbnTestPluginApi", + "section": "def-common.KbnClient", + "text": "KbnClient" + }, + "; esArchiver: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + { + "pluginId": "@kbn/es-archiver", + "scope": "common", + "docId": "kibKbnEsArchiverPluginApi", + "section": "def-common.EsArchiver", + "text": "EsArchiver" + }, + "; retry: typeof ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RetryService", + "text": "RetryService" + }, + "; supertestWithoutAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + "node_modules/@types/supertest/lib/agent", + "<", + "SuperTestStatic", + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }, {}, ProvidedTypeMap<{ es: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + "default", + "; kibanaServer: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + { + "pluginId": "@kbn/test", + "scope": "common", + "docId": "kibKbnTestPluginApi", + "section": "def-common.KbnClient", + "text": "KbnClient" + }, + "; esArchiver: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + { + "pluginId": "@kbn/es-archiver", + "scope": "common", + "docId": "kibKbnEsArchiverPluginApi", + "section": "def-common.EsArchiver", + "text": "EsArchiver" + }, + "; retry: typeof ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RetryService", + "text": "RetryService" + }, + "; supertestWithoutAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => ", + "node_modules/@types/supertest/lib/agent", + "<", + "SuperTestStatic", + ".Test>; samlAuth: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => Promise<{ getInteractiveUserSessionCookieWithRoleScope(role: string): Promise; getM2MApiCredentialsWithRoleScope(role: string): Promise<{ Cookie: string; }>; getEmail(role: string): Promise; getUserData(role: string): Promise<", + "UserProfile", + ">; createM2mApiKeyWithDefaultRoleScope(): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; createM2mApiKeyWithRoleScope(role: string): Promise<", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + ">; invalidateM2mApiKeyWithRoleScope(roleCredentials: ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.RoleCredentials", + "text": "RoleCredentials" + }, + "): Promise; getCommonRequestHeader(): { 'kbn-xsrf': string; }; getInternalRequestHeader(): ", + { + "pluginId": "@kbn/ftr-common-functional-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalServicesPluginApi", + "section": "def-common.InternalRequestHeader", + "text": "InternalRequestHeader" + }, + "; DEFAULT_ROLE: string; }>; }>, ProvidedTypeMap<{}>>" + ], + "path": "packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ], "initialIsOpen": false diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 6e8e7f3700f4f..f359cb56062f5 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.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 | |-------------------|-----------|------------------------|-----------------| -| 39 | 0 | 24 | 1 | +| 46 | 0 | 31 | 1 | ## Common @@ -31,6 +31,9 @@ Contact [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kiban ### Classes +### Interfaces + + ### Consts, variables and types diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index 619b98f6a7e75..287394ae5d9f7 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index d1dfb000ad7e0..0d2a6676daa23 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-08-06 +date: 2024-08-09 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 a2d0ea5ed9c12..f6affb2cc3901 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-08-06 +date: 2024-08-09 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 f68785590998b..e62ae05eb88e3 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_grouping.mdx b/api_docs/kbn_grouping.mdx index 1ca338e33737a..f5e67e9e0056c 100644 --- a/api_docs/kbn_grouping.mdx +++ b/api_docs/kbn_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grouping title: "@kbn/grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grouping plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grouping'] --- import kbnGroupingObj from './kbn_grouping.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 3de2f3a3eb7f2..56214b9a0a957 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-08-06 +date: 2024-08-09 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 a90d7f442f96e..f4278b57dd23c 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-08-06 +date: 2024-08-09 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 c4e29db644bcb..3f325c3286ebf 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 faf5303e3417e..4f31cadb45514 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-08-06 +date: 2024-08-09 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 fa9a9fea0860c..0210717eca361 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-08-06 +date: 2024-08-09 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 07082fad107b4..5537e3d5d8d35 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-08-06 +date: 2024-08-09 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 65afe78d5729f..9fa7d2e5a5615 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-08-06 +date: 2024-08-09 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 49899651f6b22..954c8e0027282 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-08-06 +date: 2024-08-09 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 4585c795917d4..af6d88c50c3eb 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management.mdx b/api_docs/kbn_index_management.mdx index 40693f7749a91..fd627fe773e53 100644 --- a/api_docs/kbn_index_management.mdx +++ b/api_docs/kbn_index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management title: "@kbn/index-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management'] --- import kbnIndexManagementObj from './kbn_index_management.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index 9c37a4b8f61ae..4e8ce719ee1fa 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 14b91283cffb4..e4cbca9d3634b 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-08-06 +date: 2024-08-09 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 7115666a849b4..6534f38d91464 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-08-06 +date: 2024-08-09 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 e82233d9c8edd..5627d4ada06d0 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index 4860a591221df..0f09a71e32498 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index f80dc6db3d579..813674a4abc18 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-08-06 +date: 2024-08-09 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 c361e51a35609..bb6bb9e13ae39 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-08-06 +date: 2024-08-09 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 3240cddfb86b1..6cc8901f07644 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_json_schemas.mdx b/api_docs/kbn_json_schemas.mdx index c704a1264d741..56f0c0274e5ea 100644 --- a/api_docs/kbn_json_schemas.mdx +++ b/api_docs/kbn_json_schemas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-schemas title: "@kbn/json-schemas" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-schemas plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-schemas'] --- import kbnJsonSchemasObj from './kbn_json_schemas.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 4337f00918a97..f856ccd79cf0d 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-08-06 +date: 2024-08-09 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 c2d086a2fc25b..79fd39cb3b444 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2024-08-06 +date: 2024-08-09 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 e83218564cb7c..2eabd18413b80 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index a521aea908f90..f20b453af5f0f 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index bc0615f858781..d0b28f46bb67f 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-08-06 +date: 2024-08-09 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 b419ddcb0b2e1..2bae0c016ebfb 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index dc698ee4c22b7..a0ef2c4d6316a 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index bea940fadb088..3bfea5fc1db1e 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-08-06 +date: 2024-08-09 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.devdocs.json b/api_docs/kbn_management_cards_navigation.devdocs.json index 328c9ff782cc3..1b27bb9518cfd 100644 --- a/api_docs/kbn_management_cards_navigation.devdocs.json +++ b/api_docs/kbn_management_cards_navigation.devdocs.json @@ -145,7 +145,7 @@ "label": "hideLinksTo", "description": [], "signature": [ - "(\"transform\" | \"tags\" | \"maintenanceWindows\" | \"dataViews\" | \"settings\" | \"data_quality\" | \"filesManagement\" | \"roles\" | \"reporting\" | \"api_keys\" | \"index_management\" | \"ingest_pipelines\" | \"jobsListLink\" | \"objects\" | \"pipelines\" | \"triggersActions\" | \"triggersActionsConnectors\")[] | undefined" + "(\"transform\" | \"tags\" | \"maintenanceWindows\" | \"dataViews\" | \"spaces\" | \"settings\" | \"data_quality\" | \"filesManagement\" | \"roles\" | \"reporting\" | \"api_keys\" | \"index_management\" | \"ingest_pipelines\" | \"jobsListLink\" | \"objects\" | \"pipelines\" | \"triggersActions\" | \"triggersActionsConnectors\")[] | undefined" ], "path": "packages/kbn-management/cards_navigation/src/types.ts", "deprecated": false, @@ -202,7 +202,7 @@ "label": "AppId", "description": [], "signature": [ - "\"transform\" | \"tags\" | \"maintenanceWindows\" | \"dataViews\" | \"settings\" | \"data_quality\" | \"filesManagement\" | \"roles\" | \"reporting\" | \"api_keys\" | \"index_management\" | \"ingest_pipelines\" | \"jobsListLink\" | \"objects\" | \"pipelines\" | \"triggersActions\" | \"triggersActionsConnectors\"" + "\"transform\" | \"tags\" | \"maintenanceWindows\" | \"dataViews\" | \"spaces\" | \"settings\" | \"data_quality\" | \"filesManagement\" | \"roles\" | \"reporting\" | \"api_keys\" | \"index_management\" | \"ingest_pipelines\" | \"jobsListLink\" | \"objects\" | \"pipelines\" | \"triggersActions\" | \"triggersActionsConnectors\"" ], "path": "packages/kbn-management/cards_navigation/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 11f4d0a57f360..a6a934fbec596 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-08-06 +date: 2024-08-09 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 0ebcf06b9fc7f..035dbfe2654df 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-08-06 +date: 2024-08-09 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 f92622d63fbcb..1a1389ce7b568 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-08-06 +date: 2024-08-09 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 5e617d44e4415..4961ffd1147d7 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-08-06 +date: 2024-08-09 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 995436b2e7ec8..0acaa705ce0e4 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-08-06 +date: 2024-08-09 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 d854b26c511a3..fb62c685dabdc 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-08-06 +date: 2024-08-09 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 1a4a0261d52c4..672a325bccfbb 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.devdocs.json b/api_docs/kbn_management_settings_ids.devdocs.json index 516e6a7f6bade..f5662d441bdc0 100644 --- a/api_docs/kbn_management_settings_ids.devdocs.json +++ b/api_docs/kbn_management_settings_ids.devdocs.json @@ -1072,6 +1072,66 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/management-settings-ids", + "id": "def-common.OBSERVABILITY_AI_ASSISTANT_LOGS_INDEX_PATTERN_ID", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_AI_ASSISTANT_LOGS_INDEX_PATTERN_ID", + "description": [], + "signature": [ + "\"observability:aiAssistantLogsIndexPattern\"" + ], + "path": "packages/kbn-management/settings/setting_ids/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/management-settings-ids", + "id": "def-common.OBSERVABILITY_AI_ASSISTANT_RESPONSE_LANGUAGE", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_AI_ASSISTANT_RESPONSE_LANGUAGE", + "description": [], + "signature": [ + "\"observability:aiAssistantResponseLanguage\"" + ], + "path": "packages/kbn-management/settings/setting_ids/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/management-settings-ids", + "id": "def-common.OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN", + "description": [], + "signature": [ + "\"observability:aiAssistantSearchConnectorIndexPattern\"" + ], + "path": "packages/kbn-management/settings/setting_ids/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/management-settings-ids", + "id": "def-common.OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING", + "description": [], + "signature": [ + "\"observability:aiAssistantSimulatedFunctionCalling\"" + ], + "path": "packages/kbn-management/settings/setting_ids/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/management-settings-ids", "id": "def-common.OBSERVABILITY_APM_AGENT_EXPLORER_VIEW_ID", diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index fd47ae3343d92..d0115078e8869 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 139 | 0 | 137 | 0 | +| 143 | 0 | 141 | 0 | ## Common diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 83b61fe34cbd4..23b153a08a3bc 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-08-06 +date: 2024-08-09 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 a587b6bde03f1..c7f57749ad627 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-08-06 +date: 2024-08-09 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 de2e95770f0ee..4d309d29a5eca 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-08-06 +date: 2024-08-09 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 8f26636b4a938..b77b2d65316a3 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 817e97fa2372f..d9956bfcba7c4 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-08-06 +date: 2024-08-09 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 f538f37f0031c..73b006dbaaf0a 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 4115566771042..9d5d73a033509 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 daa27259ffd6d..dd84ae65b9b61 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 6767540557ce5..0a07c49755b54 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index e9119cf25dc1a..2197d32529433 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-08-06 +date: 2024-08-09 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 7bd91e4d05e8c..87db95be070b7 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-08-06 +date: 2024-08-09 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 bdae0426f12fe..b8cd7147e4989 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 00d451bd0b329..596a091e86cfd 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-08-06 +date: 2024-08-09 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 e9bcb7e486994..880f4c21d3b43 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-08-06 +date: 2024-08-09 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 0bfcb64029262..208cc3e39e4bc 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 f9ebe0eb09486..a7305f8a4c592 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 1068fb64fd6f1..1a12e22154970 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-08-06 +date: 2024-08-09 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 eeb018dbe198f..fc6ed63a6822a 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-08-06 +date: 2024-08-09 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 7ac9910294902..d02563e7e27d8 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-08-06 +date: 2024-08-09 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 c91f9c5e667f2..704057c951f3c 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-08-06 +date: 2024-08-09 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 a440917ef6f2b..b87112e26c0d3 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-08-06 +date: 2024-08-09 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 1d0225f750fa7..a301295da765c 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-08-06 +date: 2024-08-09 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 18db1d51c8ce6..235dfb0d31f79 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 12ecc3da2e8b3..551ea1dc6caed 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 334aa1b8607cc..e5565078c25a4 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 65a4e3d3e5a35..6d77cf685cdcf 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 8acc6e1ca3916..a9960cfc51f99 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 37ada356838f1..3f469db713a08 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 1b4431efd5c74..dfec863c7b045 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.devdocs.json b/api_docs/kbn_ml_trained_models_utils.devdocs.json index ae77700bbd77e..29e317f3d5c28 100644 --- a/api_docs/kbn_ml_trained_models_utils.devdocs.json +++ b/api_docs/kbn_ml_trained_models_utils.devdocs.json @@ -178,6 +178,19 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/ml-trained-models-utils", + "id": "def-common.ModelDefinition.supported", + "type": "boolean", + "tags": [], + "label": "supported", + "description": [ + "Indicates if model version is supported by the cluster" + ], + "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.hidden", @@ -619,7 +632,7 @@ "label": "ELASTIC_MODEL_DEFINITIONS", "description": [], "signature": [ - "{ [x: string]: ", + "{ [x: string]: Omit<", { "pluginId": "@kbn/ml-trained-models-utils", "scope": "common", @@ -627,7 +640,7 @@ "section": "def-common.ModelDefinition", "text": "ModelDefinition" }, - "; }" + ", \"supported\">; }" ], "path": "x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts", "deprecated": false, diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index b9a6711005009..8ca410ad45524 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 | |-------------------|-----------|------------------------|-----------------| -| 43 | 0 | 38 | 1 | +| 44 | 0 | 38 | 1 | ## Common diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index eabdc3f3aec25..e3a889ecac84f 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-08-06 +date: 2024-08-09 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 7062d772f1634..18e180b9a4d9a 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index fb2d1fc7e3e05..f642a52cec5f7 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 97d869474c19e..3bf1a19614a44 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index be1d84258eae1..b30a3e4e8277e 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-08-06 +date: 2024-08-09 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 d9d4234d0cd38..f2723603a38cd 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_rule_utils.mdx b/api_docs/kbn_observability_alerting_rule_utils.mdx index d490f8594a98c..5b40d0a795928 100644 --- a/api_docs/kbn_observability_alerting_rule_utils.mdx +++ b/api_docs/kbn_observability_alerting_rule_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-rule-utils title: "@kbn/observability-alerting-rule-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-rule-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-rule-utils'] --- import kbnObservabilityAlertingRuleUtilsObj from './kbn_observability_alerting_rule_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 0041bbc73dfde..5b24aba396313 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-08-06 +date: 2024-08-09 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 54d482c7fbbfb..494ee2cd786d3 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-08-06 +date: 2024-08-09 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 411030dede00e..adfbc760f9616 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-08-06 +date: 2024-08-09 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 f07dc77f78de2..062b0f833553d 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-08-06 +date: 2024-08-09 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 23bce922ba194..1d98c03234012 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-08-06 +date: 2024-08-09 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 e88aee50aba39..637e067a45759 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-08-06 +date: 2024-08-09 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 cf6410385b51e..6a1c68996bf35 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-08-06 +date: 2024-08-09 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 3fb27d1fa19f1..bf1ee7759d94d 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-08-06 +date: 2024-08-09 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 19622db227deb..889a9523193d0 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 991e3b96cff40..35306a7857640 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index dde7ca3aaa8fb..db1c6f856552e 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-08-06 +date: 2024-08-09 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 70c1cb28af507..a33156ba9da50 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index bc5be68622a41..2b340ad57b80a 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 274f7cb055450..5be8284b5e1b1 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 66604b54f662a..a0640d0df6ddc 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index dce897a321b11..8a53692389400 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-08-06 +date: 2024-08-09 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 a1c3b492e3ca8..e310e28e31994 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 1acc0a8695177..0fcfd3f18eb11 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 9a1558962685a..5819ec07359f1 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-08-06 +date: 2024-08-09 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 10e841bc3f9f3..6a59a646850ca 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-08-06 +date: 2024-08-09 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 a8b9ae39f16e5..9d956d3f8b7cb 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-08-06 +date: 2024-08-09 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 e7da411e9ac7b..eb7f6ad6b7a0e 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-08-06 +date: 2024-08-09 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 096bfcd11f537..cc5d88ceaeabc 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-08-06 +date: 2024-08-09 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 b48adadb5f8a2..ab68c418d0127 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_recently_accessed.mdx b/api_docs/kbn_recently_accessed.mdx index f2ba6e35faaa3..31b028cf26375 100644 --- a/api_docs/kbn_recently_accessed.mdx +++ b/api_docs/kbn_recently_accessed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-recently-accessed title: "@kbn/recently-accessed" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/recently-accessed plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/recently-accessed'] --- import kbnRecentlyAccessedObj from './kbn_recently_accessed.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index b7ed61119cc5c..50709a04232fe 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-08-06 +date: 2024-08-09 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 ea7c6fe23f05e..c5ff895887cb1 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-08-06 +date: 2024-08-09 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 3bdfbd5196454..762f0c2346e0b 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-08-06 +date: 2024-08-09 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 6d4417d848d9f..38e84e9a5b618 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-08-06 +date: 2024-08-09 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 d78f92955a580..1a1457b4e9810 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 04ab65e55bd50..172e86fdd7e21 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index ae5a1488f94d9..c913fba84c84b 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 89f21df90544d..56b80eef5264b 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index 29c072a5a7431..0e35216eed852 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-08-06 +date: 2024-08-09 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 df2eea6429908..cdf4cbb333701 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-08-06 +date: 2024-08-09 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 eeb1238575ccb..e8b47eeea570d 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-08-06 +date: 2024-08-09 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 0ec7efbb6e85a..74e18a82a6360 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-08-06 +date: 2024-08-09 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 5e1223498c74c..9c7941d91bb30 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index d0958a53372d0..f4f8d5d69ffc9 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index 61e11189d5a7d..142b5872b89a9 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-08-06 +date: 2024-08-09 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 b62c1f13de9a1..6f89adb019a4e 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_response_ops_feature_flag_service.mdx b/api_docs/kbn_response_ops_feature_flag_service.mdx index 0be24ade4ba4e..3eb25f6920210 100644 --- a/api_docs/kbn_response_ops_feature_flag_service.mdx +++ b/api_docs/kbn_response_ops_feature_flag_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-feature-flag-service title: "@kbn/response-ops-feature-flag-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-feature-flag-service plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-feature-flag-service'] --- import kbnResponseOpsFeatureFlagServiceObj from './kbn_response_ops_feature_flag_service.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index d7900b084171e..8fa39beb05e1d 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rollup.mdx b/api_docs/kbn_rollup.mdx index 38751c8833e40..b0dff4db3696d 100644 --- a/api_docs/kbn_rollup.mdx +++ b/api_docs/kbn_rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rollup title: "@kbn/rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rollup plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rollup'] --- import kbnRollupObj from './kbn_rollup.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index dde9b152f5953..3e9240b8580ef 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 61d60fe8bdaa7..230a004a69bd3 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 81de839fbe10c..08be4922ad817 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 0b47fd41e576c..a05072af3ca45 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 25b6bec08671a..29ec4ff50e200 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 3dc6e9868ca39..8c25bb9ec32fc 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index a21a367e93ced..ec6ee3bed6a2e 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 550bb6e68d724..0952e04990691 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index 7118722d6d710..ba4f70ac49695 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-08-06 +date: 2024-08-09 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 bdfcc5b43eb89..63325c095d147 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 9278cf6f06f86..b1d7183b31cb8 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_api_key_management.mdx b/api_docs/kbn_security_api_key_management.mdx index 26f3c031bb18b..3fd70e6f78d70 100644 --- a/api_docs/kbn_security_api_key_management.mdx +++ b/api_docs/kbn_security_api_key_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-api-key-management title: "@kbn/security-api-key-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-api-key-management plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-api-key-management'] --- import kbnSecurityApiKeyManagementObj from './kbn_security_api_key_management.devdocs.json'; diff --git a/api_docs/kbn_security_form_components.mdx b/api_docs/kbn_security_form_components.mdx index 8a618a6e36f3f..b37f088ed2295 100644 --- a/api_docs/kbn_security_form_components.mdx +++ b/api_docs/kbn_security_form_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-form-components title: "@kbn/security-form-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-form-components plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-form-components'] --- import kbnSecurityFormComponentsObj from './kbn_security_form_components.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 71a22be7263be..577bd0e92d33d 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index b53c79874efb9..31223a90a9992 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-08-06 +date: 2024-08-09 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 2c9cc27296526..462a7906a761c 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-08-06 +date: 2024-08-09 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 f0d136cc60579..88b9f59719293 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-08-06 +date: 2024-08-09 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_distribution_bar.mdx b/api_docs/kbn_security_solution_distribution_bar.mdx index 18cf95250b003..9f1343fe17f2a 100644 --- a/api_docs/kbn_security_solution_distribution_bar.mdx +++ b/api_docs/kbn_security_solution_distribution_bar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-distribution-bar title: "@kbn/security-solution-distribution-bar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-distribution-bar plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-distribution-bar'] --- import kbnSecuritySolutionDistributionBarObj from './kbn_security_solution_distribution_bar.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index 2ff538d2b1fae..264eec3c04698 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-08-06 +date: 2024-08-09 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 938a2b0de1fee..b0b80a300cafd 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-08-06 +date: 2024-08-09 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 0aea24e8a82d7..cc03821702323 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-08-06 +date: 2024-08-09 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 0b91b88a15b11..14972b29c38fb 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-08-06 +date: 2024-08-09 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 f6ef0056db310..dd1feccd52ab7 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-08-06 +date: 2024-08-09 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 ddd2999d29d37..f9e9d3315803b 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-08-06 +date: 2024-08-09 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 a7a1409c7e530..7aac9d824e159 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-08-06 +date: 2024-08-09 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 939dd5e916e5c..cd0d6ead49d09 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 1f9c44937ea70..fa61ae7f0558f 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.devdocs.json +++ b/api_docs/kbn_securitysolution_exception_list_components.devdocs.json @@ -851,7 +851,7 @@ "label": "formattedDateComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"html\" | \"stop\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"html\" | \"stop\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx", "deprecated": false, @@ -865,7 +865,7 @@ "label": "securityLinkAnchorComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"html\" | \"stop\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"html\" | \"stop\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx", "deprecated": false, @@ -1004,7 +1004,7 @@ "label": "securityLinkAnchorComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"html\" | \"stop\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"html\" | \"stop\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", "deprecated": false, @@ -1018,7 +1018,7 @@ "label": "formattedDateComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"html\" | \"stop\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"html\" | \"stop\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", "deprecated": false, @@ -1144,7 +1144,7 @@ "label": "showValueListModal", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"html\" | \"stop\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"source\" | \"meta\" | \"desc\" | \"filter\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"text\" | \"map\" | \"head\" | \"slot\" | \"style\" | \"title\" | \"data\" | \"path\" | \"code\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"q\" | \"body\" | \"html\" | \"stop\" | \"main\" | \"form\" | \"line\" | \"rect\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"base\" | React.ComponentType | \"s\" | \"legend\" | \"canvas\" | \"svg\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"table\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"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\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"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 36f666bf0d5b1..661a2252abca6 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 6001271b0ede2..6b65540844883 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-08-06 +date: 2024-08-09 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.devdocs.json b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json index 38fe460b060e7..d705674f3eb19 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json @@ -987,7 +987,7 @@ "label": "Type", "description": [], "signature": [ - "\"eql\" | \"esql\" | \"query\" | \"threshold\" | \"saved_query\" | \"threat_match\" | \"machine_learning\" | \"new_terms\"" + "\"eql\" | \"esql\" | \"query\" | \"threshold\" | \"threat_match\" | \"saved_query\" | \"machine_learning\" | \"new_terms\"" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts", "deprecated": false, @@ -1002,7 +1002,7 @@ "label": "TypeOrUndefined", "description": [], "signature": [ - "\"eql\" | \"esql\" | \"query\" | \"threshold\" | \"saved_query\" | \"threat_match\" | \"machine_learning\" | \"new_terms\" | undefined" + "\"eql\" | \"esql\" | \"query\" | \"threshold\" | \"threat_match\" | \"saved_query\" | \"machine_learning\" | \"new_terms\" | undefined" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/type/index.ts", "deprecated": false, diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index c750aff5ca268..1c0d6a6d7bc8a 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-08-06 +date: 2024-08-09 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 4fd3e643671e3..28bbb1cb50c70 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-08-06 +date: 2024-08-09 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 3a31932b548ee..78871781a9ee2 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-08-06 +date: 2024-08-09 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 2a70f38373654..2759da152787e 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-08-06 +date: 2024-08-09 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 88174de04865c..9b564765be866 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-08-06 +date: 2024-08-09 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 ec6b73da8a2b8..8a36f10caa10c 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-08-06 +date: 2024-08-09 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 02645f20cc54c..b96366d3d0209 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-08-06 +date: 2024-08-09 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 3baf1593b3afb..8bfdb68f00eba 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-08-06 +date: 2024-08-09 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.devdocs.json b/api_docs/kbn_securitysolution_rules.devdocs.json index 3b513ef6f5670..15c7914511c23 100644 --- a/api_docs/kbn_securitysolution_rules.devdocs.json +++ b/api_docs/kbn_securitysolution_rules.devdocs.json @@ -75,7 +75,7 @@ "label": "isRuleType", "description": [], "signature": [ - "(ruleType: unknown) => ruleType is \"eql\" | \"esql\" | \"query\" | \"threshold\" | \"saved_query\" | \"threat_match\" | \"machine_learning\" | \"new_terms\"" + "(ruleType: unknown) => ruleType is \"eql\" | \"esql\" | \"query\" | \"threshold\" | \"threat_match\" | \"saved_query\" | \"machine_learning\" | \"new_terms\"" ], "path": "packages/kbn-securitysolution-rules/src/utils.ts", "deprecated": false, @@ -259,7 +259,7 @@ "label": "RuleType", "description": [], "signature": [ - "\"eql\" | \"esql\" | \"query\" | \"threshold\" | \"saved_query\" | \"threat_match\" | \"machine_learning\" | \"new_terms\"" + "\"eql\" | \"esql\" | \"query\" | \"threshold\" | \"threat_match\" | \"saved_query\" | \"machine_learning\" | \"new_terms\"" ], "path": "packages/kbn-securitysolution-rules/src/rule_type_mappings.ts", "deprecated": false, diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index e3a51c196bd94..e45dce53c9560 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-08-06 +date: 2024-08-09 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 877cfbd3428fa..3754426f39521 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.devdocs.json b/api_docs/kbn_securitysolution_utils.devdocs.json index d36fe39b6481f..7f9eeb7c9d58c 100644 --- a/api_docs/kbn_securitysolution_utils.devdocs.json +++ b/api_docs/kbn_securitysolution_utils.devdocs.json @@ -251,6 +251,53 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.isAggregatingQuery", + "type": "Function", + "tags": [], + "label": "isAggregatingQuery", + "description": [], + "signature": [ + "(ast: ", + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLAst", + "text": "ESQLAst" + }, + ") => boolean" + ], + "path": "packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-utils", + "id": "def-common.isAggregatingQuery.$1", + "type": "Array", + "tags": [], + "label": "ast", + "description": [], + "signature": [ + { + "pluginId": "@kbn/esql-ast", + "scope": "common", + "docId": "kibKbnEsqlAstPluginApi", + "section": "def-common.ESQLAst", + "text": "ESQLAst" + } + ], + "path": "packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-utils", "id": "def-common.isPathValid", diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 6947c0a46e154..5a0a585cc9326 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-detection-engine](https://github.com/orgs/elastic/tea | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 49 | 0 | 44 | 0 | +| 51 | 0 | 46 | 0 | ## Common diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 06c2357974ae9..80394e07bce98 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-08-06 +date: 2024-08-09 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 30b76070d4222..dcbc0bbe2c3dd 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_utils.mdx b/api_docs/kbn_server_route_repository_utils.mdx index 0d017f71736cb..54e0938623d67 100644 --- a/api_docs/kbn_server_route_repository_utils.mdx +++ b/api_docs/kbn_server_route_repository_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-utils title: "@kbn/server-route-repository-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-utils'] --- import kbnServerRouteRepositoryUtilsObj from './kbn_server_route_repository_utils.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 662a539e00656..ce383973e02de 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-08-06 +date: 2024-08-09 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 a8a4dc56b1831..51976aeada24d 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-08-06 +date: 2024-08-09 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 5f49432c5b8e0..03346f1128546 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-08-06 +date: 2024-08-09 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 d4b96d1fd683c..d8e1b331ff958 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-08-06 +date: 2024-08-09 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 c60b639ebc0d4..f63210dcce3c5 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-08-06 +date: 2024-08-09 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 573792a50c81b..b77df2cc86320 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-08-06 +date: 2024-08-09 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 efb332805c963..35796b57b0599 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-08-06 +date: 2024-08-09 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 1087348dbd964..0f51bd3433a54 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-08-06 +date: 2024-08-09 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 ffd5e576acc90..3cb82b7af71ed 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index d05770a209263..68cd659c14da0 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-08-06 +date: 2024-08-09 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 853c8e56f0d5d..ddb10f980cc7d 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-08-06 +date: 2024-08-09 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 1920e7a599553..0c032e48c014a 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 fe4b13d8aa767..56f093c4688c6 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-08-06 +date: 2024-08-09 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 f805b525f70d0..301470d5c9882 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-08-06 +date: 2024-08-09 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 d7acf0f27ce37..52acc4d1b2639 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-08-06 +date: 2024-08-09 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 f6678dfc9737e..aa7d166898a2d 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-08-06 +date: 2024-08-09 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 9f435d7e1e6d3..7b524e1a65ea7 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 3615b0324cac9..f8c7d9c4668ab 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 0ac7b07409960..ca75b3b573c4f 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-08-06 +date: 2024-08-09 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 ea31b1225a045..ad18cbc4b95f2 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-08-06 +date: 2024-08-09 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 ecdae280411aa..68ff7db54045d 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-08-06 +date: 2024-08-09 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 30637715fbe72..416222fe0f420 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-08-06 +date: 2024-08-09 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 217d86c11589d..ff0df39065e16 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-08-06 +date: 2024-08-09 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 cbf0737b31e50..d8991488c0dd6 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 c6184ca9258ab..75f556420d56c 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-08-06 +date: 2024-08-09 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 bf060a711aaf0..4da52f40a26ca 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 7015fcce844ff..00630609117a2 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-08-06 +date: 2024-08-09 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 076585e40c4cc..e0fde1a8cafff 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 33a1daa4591e9..009f561f63c03 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-08-06 +date: 2024-08-09 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 f3423fe27e389..ed13cb061ea95 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 9a734915ae043..84617395fe9a3 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-08-06 +date: 2024-08-09 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 3455e16db48c3..9fd250b69e3df 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 a98ff4368db02..ea30e7e0f16ca 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-08-06 +date: 2024-08-09 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 5f0559b33f54c..155c09856b70b 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-08-06 +date: 2024-08-09 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 585991219dbfa..4bf5b914cb22e 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 ab8488daf7ae6..8c84a656ab65d 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 15ee5f80b48d8..deefd7e2a7845 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-08-06 +date: 2024-08-09 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 7ca83d5934d9b..09c30f5ab939b 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-08-06 +date: 2024-08-09 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 268e9a3117051..fd32b528733c2 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 65f5989e0543d..b053e0e22afba 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-08-06 +date: 2024-08-09 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 563dbcaaa5454..f3d5a5b3c1db6 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-08-06 +date: 2024-08-09 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 96c8617fc7e31..550d9812a14eb 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-08-06 +date: 2024-08-09 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 6a9b6754fdc7e..f620b34a00479 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-08-06 +date: 2024-08-09 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 8d46467f81523..911369ac4943e 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index c7419434379af..fdf87392677a3 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index dfcb889b32db8..509fe1e863de9 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-08-06 +date: 2024-08-09 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 77f3590eb32c0..dad426dd249e5 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -606,7 +606,7 @@ "label": "CreateSLOInput", "description": [], "signature": [ - "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; } & { id?: string | undefined; settings?: { syncDelay?: string | undefined; frequency?: string | undefined; preventInitialBackfill?: boolean | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | string[] | undefined; revision?: number | undefined; }" + "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; } & { id?: string | undefined; settings?: { syncDelay?: string | undefined; frequency?: string | undefined; preventInitialBackfill?: boolean | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | string[] | undefined; revision?: number | undefined; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/create.ts", "deprecated": false, @@ -621,7 +621,7 @@ "label": "CreateSLOParams", "description": [], "signature": [ - "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: ", + "{ name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: ", { "pluginId": "@kbn/slo-schema", "scope": "common", @@ -835,7 +835,7 @@ "label": "FindSLODefinitionsResponse", "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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }[]; }" + "{ 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }[]; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/find_definition.ts", "deprecated": false, @@ -895,7 +895,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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: string; } | undefined; })[]; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/find.ts", "deprecated": false, @@ -910,7 +910,7 @@ "label": "GetPreviewDataParams", "description": [], "signature": [ - "{ indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; range: { from: Date; to: Date; }; } & { objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", + "{ indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; range: { from: Date; to: Date; }; } & { objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", { "pluginId": "@kbn/slo-schema", "scope": "common", @@ -993,7 +993,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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: string; } | undefined; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/get.ts", "deprecated": false, @@ -1098,7 +1098,7 @@ "label": "Indicator", "description": [], "signature": [ - "{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }" + "{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/indicators.ts", "deprecated": false, @@ -1263,7 +1263,7 @@ "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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }" + "{ 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/reset.ts", "deprecated": false, @@ -1278,7 +1278,7 @@ "label": "SLODefinitionResponse", "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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }" + "{ 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -1293,7 +1293,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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: string; } | undefined; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -1338,7 +1338,7 @@ "label": "TimesliceMetricBasicMetricWithField", "description": [], "signature": [ - "{ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }" + "{ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/indicators.ts", "deprecated": false, @@ -1368,7 +1368,7 @@ "label": "TimesliceMetricIndicator", "description": [], "signature": [ - "{ type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/indicators.ts", "deprecated": false, @@ -1398,7 +1398,7 @@ "label": "UpdateSLOInput", "description": [], "signature": [ - "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | undefined; timeWindow?: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; } | undefined; budgetingMethod?: \"occurrences\" | \"timeslices\" | undefined; objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }) | undefined; settings?: { syncDelay?: string | undefined; frequency?: string | undefined; preventInitialBackfill?: boolean | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | string[] | undefined; }" + "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | undefined; timeWindow?: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; } | undefined; budgetingMethod?: \"occurrences\" | \"timeslices\" | undefined; objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }) | undefined; settings?: { syncDelay?: string | undefined; frequency?: string | undefined; preventInitialBackfill?: boolean | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | string[] | undefined; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/update.ts", "deprecated": false, @@ -1413,7 +1413,7 @@ "label": "UpdateSLOParams", "description": [], "signature": [ - "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | undefined; timeWindow?: { duration: ", + "{ name?: string | undefined; description?: string | undefined; indicator?: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | undefined; timeWindow?: { duration: ", { "pluginId": "@kbn/slo-schema", "scope": "common", @@ -1468,7 +1468,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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }" + "{ 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/update.ts", "deprecated": false, diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index ec8ef0d741d4f..ffaecd43fb361 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 8aed8246479f6..b7ac8544c18d5 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index f01b98b21fe8e..bf30651905a68 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 97653ac5f48ef..b19632c57f329 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-08-06 +date: 2024-08-09 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 38358d7eddb12..9f0093dbdb0e2 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-08-06 +date: 2024-08-09 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 eb28f00d1ff49..e1ffd1f71bdd4 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_synthetics_e2e.mdx b/api_docs/kbn_synthetics_e2e.mdx index 26198f60aeda8..12f115f2dbe7e 100644 --- a/api_docs/kbn_synthetics_e2e.mdx +++ b/api_docs/kbn_synthetics_e2e.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-e2e title: "@kbn/synthetics-e2e" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-e2e plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-e2e'] --- import kbnSyntheticsE2eObj from './kbn_synthetics_e2e.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 299869f94c2ab..e0a07799c07f6 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-08-06 +date: 2024-08-09 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 50380d3b663e5..a8df40b09e765 100644 --- a/api_docs/kbn_test.devdocs.json +++ b/api_docs/kbn_test.devdocs.json @@ -1699,21 +1699,6 @@ "deprecated": false, "trackAdoption": false, "isRequired": true - }, - { - "parentPluginId": "@kbn/test", - "id": "def-common.SamlSessionManager.Unnamed.$2", - "type": "string", - "tags": [], - "label": "rolesFilename", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/kbn-test/src/auth/session_manager.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false } ], "returnComment": [] @@ -5077,17 +5062,29 @@ { "parentPluginId": "@kbn/test", "id": "def-common.SamlSessionManagerOptions.supportedRoles", - "type": "Array", + "type": "Object", "tags": [], "label": "supportedRoles", "description": [], "signature": [ - "string[] | undefined" + "SupportedRoles", + " | undefined" ], "path": "packages/kbn-test/src/auth/session_manager.ts", "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManagerOptions.cloudUsersFilePath", + "type": "string", + "tags": [], + "label": "cloudUsersFilePath", + "description": [], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/test", "id": "def-common.SamlSessionManagerOptions.log", diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 0f0da04998a3b..10570bb82a120 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-08-06 +date: 2024-08-09 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 | |-------------------|-----------|------------------------|-----------------| -| 315 | 4 | 267 | 13 | +| 315 | 4 | 267 | 14 | ## Common diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index ae05947753d9c..ce326a644d56d 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 04b9d4388b9e6..d4791f44e8c64 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2024-08-06 +date: 2024-08-09 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 6ab97d023694a..ed5fab2876211 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2024-08-06 +date: 2024-08-09 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 5458fa498636d..bd2353284b1df 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index 735eb79c9f7b9..4846098065625 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index b45934a599fbd..6a1ba249ead24 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2024-08-06 +date: 2024-08-09 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 8071b66f52b62..20bdb0582855e 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index 6d6a5ddf91a27..49bdc67c547f5 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 82c6116592a3d..0d7f43d8a4aee 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2024-08-06 +date: 2024-08-09 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 622a8cbc9974c..c34f69600296c 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2024-08-06 +date: 2024-08-09 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 f2f3b0c780898..9616543f1d14c 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index b90fc2b008874..976e97f043fd2 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 381a1e48c8896..3137a1ee30333 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2024-08-06 +date: 2024-08-09 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.devdocs.json b/api_docs/kbn_unified_data_table.devdocs.json index c778b833f799d..00dea271a943c 100644 --- a/api_docs/kbn_unified_data_table.devdocs.json +++ b/api_docs/kbn_unified_data_table.devdocs.json @@ -11,7 +11,9 @@ "label": "DataTableRowControl", "description": [], "signature": [ - "({ children }: { children: React.ReactNode; }) => JSX.Element" + "({ size, children }: React.PropsWithChildren<{ size?: ", + "Size", + " | undefined; }>) => JSX.Element" ], "path": "packages/kbn-unified-data-table/src/components/data_table_row_control.tsx", "deprecated": false, @@ -20,29 +22,19 @@ { "parentPluginId": "@kbn/unified-data-table", "id": "def-public.DataTableRowControl.$1", - "type": "Object", + "type": "CompoundType", "tags": [], - "label": "{ children }", + "label": "{ size, children }", "description": [], + "signature": [ + "React.PropsWithChildren<{ size?: ", + "Size", + " | undefined; }>" + ], "path": "packages/kbn-unified-data-table/src/components/data_table_row_control.tsx", "deprecated": false, "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.DataTableRowControl.$1.children", - "type": "CompoundType", - "tags": [], - "label": "children", - "description": [], - "signature": [ - "boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined" - ], - "path": "packages/kbn-unified-data-table/src/components/data_table_row_control.tsx", - "deprecated": false, - "trackAdoption": false - } - ] + "isRequired": true } ], "returnComment": [], @@ -516,7 +508,7 @@ "label": "UnifiedDataTable", "description": [], "signature": [ - "({ ariaLabelledBy, columns, columnsMeta, showColumnTokens, configHeaderRowHeight, headerRowHeightState, onUpdateHeaderRowHeight, controlColumnIds, dataView, loadingState, onFilter, onResize, onSetColumns, onSort, rows, searchDescription, searchTitle, settings, showTimeCol, showFullScreenButton, sort, useNewFieldsApi, isSortEnabled, isPaginationEnabled, cellActionsTriggerId, className, rowHeightState, onUpdateRowHeight, maxAllowedSampleSize, sampleSizeState, onUpdateSampleSize, isPlainRecord, rowsPerPageState, onUpdateRowsPerPage, onFieldEdited, services, renderCustomGridBody, renderCustomToolbar, trailingControlColumns, totalHits, onFetchMoreRecords, renderDocumentView, setExpandedDoc, expandedDoc, configRowHeight, showMultiFields, maxDocFieldsDisplayed, externalControlColumns, externalAdditionalControls, rowsPerPageOptions, visibleCellActions, externalCustomRenderers, additionalFieldGroups, consumer, componentsTourSteps, gridStyleOverride, rowLineHeightOverride, cellActionsMetadata, customGridColumnsConfiguration, customControlColumnsConfiguration, enableComparisonMode, cellContext, renderCellPopover, getRowIndicator, }: ", + "({ ariaLabelledBy, columns, columnsMeta, showColumnTokens, configHeaderRowHeight, headerRowHeightState, onUpdateHeaderRowHeight, controlColumnIds, rowAdditionalLeadingControls, dataView, loadingState, onFilter, onResize, onSetColumns, onSort, rows, searchDescription, searchTitle, settings, showTimeCol, showFullScreenButton, sort, useNewFieldsApi, isSortEnabled, isPaginationEnabled, cellActionsTriggerId, className, rowHeightState, onUpdateRowHeight, maxAllowedSampleSize, sampleSizeState, onUpdateSampleSize, isPlainRecord, rowsPerPageState, onUpdateRowsPerPage, onFieldEdited, services, renderCustomGridBody, renderCustomToolbar, externalControlColumns, trailingControlColumns, totalHits, onFetchMoreRecords, renderDocumentView, setExpandedDoc, expandedDoc, configRowHeight, showMultiFields, maxDocFieldsDisplayed, externalAdditionalControls, rowsPerPageOptions, visibleCellActions, externalCustomRenderers, additionalFieldGroups, consumer, componentsTourSteps, gridStyleOverride, rowLineHeightOverride, cellActionsMetadata, customGridColumnsConfiguration, enableComparisonMode, cellContext, renderCellPopover, getRowIndicator, }: ", { "pluginId": "@kbn/unified-data-table", "scope": "public", @@ -535,7 +527,7 @@ "id": "def-public.UnifiedDataTable.$1", "type": "Object", "tags": [], - "label": "{\n ariaLabelledBy,\n columns,\n columnsMeta,\n showColumnTokens,\n configHeaderRowHeight,\n headerRowHeightState,\n onUpdateHeaderRowHeight,\n controlColumnIds = CONTROL_COLUMN_IDS_DEFAULT,\n dataView,\n loadingState,\n onFilter,\n onResize,\n onSetColumns,\n onSort,\n rows,\n searchDescription,\n searchTitle,\n settings,\n showTimeCol,\n showFullScreenButton = true,\n sort,\n useNewFieldsApi,\n isSortEnabled = true,\n isPaginationEnabled = true,\n cellActionsTriggerId,\n className,\n rowHeightState,\n onUpdateRowHeight,\n maxAllowedSampleSize,\n sampleSizeState,\n onUpdateSampleSize,\n isPlainRecord = false,\n rowsPerPageState,\n onUpdateRowsPerPage,\n onFieldEdited,\n services,\n renderCustomGridBody,\n renderCustomToolbar,\n trailingControlColumns,\n totalHits,\n onFetchMoreRecords,\n renderDocumentView,\n setExpandedDoc,\n expandedDoc,\n configRowHeight,\n showMultiFields = true,\n maxDocFieldsDisplayed = 50,\n externalControlColumns,\n externalAdditionalControls,\n rowsPerPageOptions,\n visibleCellActions,\n externalCustomRenderers,\n additionalFieldGroups,\n consumer = 'discover',\n componentsTourSteps,\n gridStyleOverride,\n rowLineHeightOverride,\n cellActionsMetadata,\n customGridColumnsConfiguration,\n customControlColumnsConfiguration,\n enableComparisonMode,\n cellContext,\n renderCellPopover,\n getRowIndicator,\n}", + "label": "{\n ariaLabelledBy,\n columns,\n columnsMeta,\n showColumnTokens,\n configHeaderRowHeight,\n headerRowHeightState,\n onUpdateHeaderRowHeight,\n controlColumnIds = CONTROL_COLUMN_IDS_DEFAULT,\n rowAdditionalLeadingControls,\n dataView,\n loadingState,\n onFilter,\n onResize,\n onSetColumns,\n onSort,\n rows,\n searchDescription,\n searchTitle,\n settings,\n showTimeCol,\n showFullScreenButton = true,\n sort,\n useNewFieldsApi,\n isSortEnabled = true,\n isPaginationEnabled = true,\n cellActionsTriggerId,\n className,\n rowHeightState,\n onUpdateRowHeight,\n maxAllowedSampleSize,\n sampleSizeState,\n onUpdateSampleSize,\n isPlainRecord = false,\n rowsPerPageState,\n onUpdateRowsPerPage,\n onFieldEdited,\n services,\n renderCustomGridBody,\n renderCustomToolbar,\n externalControlColumns, // TODO: deprecate in favor of rowAdditionalLeadingControls\n trailingControlColumns, // TODO: deprecate in favor of rowAdditionalLeadingControls\n totalHits,\n onFetchMoreRecords,\n renderDocumentView,\n setExpandedDoc,\n expandedDoc,\n configRowHeight,\n showMultiFields = true,\n maxDocFieldsDisplayed = 50,\n externalAdditionalControls,\n rowsPerPageOptions,\n visibleCellActions,\n externalCustomRenderers,\n additionalFieldGroups,\n consumer = 'discover',\n componentsTourSteps,\n gridStyleOverride,\n rowLineHeightOverride,\n cellActionsMetadata,\n customGridColumnsConfiguration,\n enableComparisonMode,\n cellContext,\n renderCellPopover,\n getRowIndicator,\n}", "description": [], "signature": [ { @@ -592,10 +584,10 @@ "interfaces": [ { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.ControlColumns", + "id": "def-public.CustomGridColumnProps", "type": "Interface", "tags": [], - "label": "ControlColumns", + "label": "CustomGridColumnProps", "description": [], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, @@ -603,13 +595,13 @@ "children": [ { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.ControlColumns.select", + "id": "def-public.CustomGridColumnProps.column", "type": "Object", "tags": [], - "label": "select", + "label": "column", "description": [], "signature": [ - "EuiDataGridControlColumn" + "EuiDataGridColumn" ], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, @@ -617,13 +609,13 @@ }, { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.ControlColumns.openDetails", - "type": "Object", + "id": "def-public.CustomGridColumnProps.headerRowHeight", + "type": "number", "tags": [], - "label": "openDetails", + "label": "headerRowHeight", "description": [], "signature": [ - "EuiDataGridControlColumn" + "number | undefined" ], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, @@ -634,10 +626,10 @@ }, { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.ControlColumnsProps", + "id": "def-public.RowControlColumn", "type": "Interface", "tags": [], - "label": "ControlColumnsProps", + "label": "RowControlColumn", "description": [], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, @@ -645,33 +637,124 @@ "children": [ { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.ControlColumnsProps.controlColumns", - "type": "Object", + "id": "def-public.RowControlColumn.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlColumn.headerAriaLabel", + "type": "string", "tags": [], - "label": "controlColumns", + "label": "headerAriaLabel", + "description": [], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlColumn.headerCellRender", + "type": "CompoundType", + "tags": [], + "label": "headerCellRender", + "description": [], + "signature": [ + "React.ComponentType<{}> | undefined" + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlColumn.renderControl", + "type": "Function", + "tags": [], + "label": "renderControl", "description": [], "signature": [ + "(Control: ", { "pluginId": "@kbn/unified-data-table", "scope": "public", "docId": "kibKbnUnifiedDataTablePluginApi", - "section": "def-public.ControlColumns", - "text": "ControlColumns" - } + "section": "def-public.RowControlComponent", + "text": "RowControlComponent" + }, + ", props: ", + { + "pluginId": "@kbn/unified-data-table", + "scope": "public", + "docId": "kibKbnUnifiedDataTablePluginApi", + "section": "def-public.RowControlRowProps", + "text": "RowControlRowProps" + }, + ") => React.ReactElement>" ], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlColumn.renderControl.$1", + "type": "Function", + "tags": [], + "label": "Control", + "description": [], + "signature": [ + { + "pluginId": "@kbn/unified-data-table", + "scope": "public", + "docId": "kibKbnUnifiedDataTablePluginApi", + "section": "def-public.RowControlComponent", + "text": "RowControlComponent" + } + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlColumn.renderControl.$2", + "type": "Object", + "tags": [], + "label": "props", + "description": [], + "signature": [ + { + "pluginId": "@kbn/unified-data-table", + "scope": "public", + "docId": "kibKbnUnifiedDataTablePluginApi", + "section": "def-public.RowControlRowProps", + "text": "RowControlRowProps" + } + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false }, { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.CustomGridColumnProps", + "id": "def-public.RowControlProps", "type": "Interface", "tags": [], - "label": "CustomGridColumnProps", + "label": "RowControlProps", "description": [], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, @@ -679,13 +762,13 @@ "children": [ { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.CustomGridColumnProps.column", - "type": "Object", + "id": "def-public.RowControlProps.datatestsubj", + "type": "string", "tags": [], - "label": "column", + "label": "'data-test-subj'", "description": [], "signature": [ - "EuiDataGridColumn" + "string | undefined" ], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, @@ -693,13 +776,119 @@ }, { "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.CustomGridColumnProps.headerRowHeight", + "id": "def-public.RowControlProps.color", + "type": "CompoundType", + "tags": [], + "label": "color", + "description": [], + "signature": [ + "\"text\" | \"warning\" | \"success\" | \"primary\" | \"accent\" | \"danger\" | undefined" + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlProps.disabled", + "type": "CompoundType", + "tags": [], + "label": "disabled", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlProps.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlProps.iconType", + "type": "CompoundType", + "tags": [], + "label": "iconType", + "description": [], + "signature": [ + "string | React.ComponentType<{}>" + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlProps.onClick", + "type": "Function", + "tags": [], + "label": "onClick", + "description": [], + "signature": [ + "((props: ", + { + "pluginId": "@kbn/unified-data-table", + "scope": "public", + "docId": "kibKbnUnifiedDataTablePluginApi", + "section": "def-public.RowControlRowProps", + "text": "RowControlRowProps" + }, + ") => void) | undefined" + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlRowProps", + "type": "Interface", + "tags": [], + "label": "RowControlRowProps", + "description": [], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlRowProps.rowIndex", "type": "number", "tags": [], - "label": "headerRowHeight", + "label": "rowIndex", + "description": [], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlRowProps.record", + "type": "Object", + "tags": [], + "label": "record", "description": [], "signature": [ - "number | undefined" + { + "pluginId": "@kbn/discover-utils", + "scope": "common", + "docId": "kibKbnDiscoverUtilsPluginApi", + "section": "def-common.DataTableRecord", + "text": "DataTableRecord" + } ], "path": "packages/kbn-unified-data-table/src/types.ts", "deprecated": false, @@ -1955,10 +2144,37 @@ "parentPluginId": "@kbn/unified-data-table", "id": "def-public.UnifiedDataTableProps.externalControlColumns", "type": "Array", - "tags": [], + "tags": [ + "deprecated" + ], "label": "externalControlColumns", + "description": [], + "signature": [ + "EuiDataGridControlColumn", + "[] | undefined" + ], + "path": "packages/kbn-unified-data-table/src/components/data_table.tsx", + "deprecated": true, + "trackAdoption": false, + "references": [ + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/components/cloud_security_data_table/cloud_security_data_table.tsx" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx" + } + ] + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.UnifiedDataTableProps.trailingControlColumns", + "type": "Array", + "tags": [], + "label": "trailingControlColumns", "description": [ - "\nOptional value for providing EuiDataGridControlColumn list of the additional leading control columns. UnifiedDataTable includes two control columns: Open Details and Select." + "\nAn optional list of the EuiDataGridControlColumn type for setting trailing control columns standard for EuiDataGrid.\nWe recommend to rather position all controls in the beginning of rows and use `rowAdditionalLeadingControls` for that\nas number of columns can be dynamically changed and we don't want the controls to become hidden due to horizontal scroll." ], "signature": [ "EuiDataGridControlColumn", @@ -1968,6 +2184,29 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.UnifiedDataTableProps.rowAdditionalLeadingControls", + "type": "Array", + "tags": [], + "label": "rowAdditionalLeadingControls", + "description": [ + "\nOptional value to extend the list of default row actions" + ], + "signature": [ + { + "pluginId": "@kbn/unified-data-table", + "scope": "public", + "docId": "kibKbnUnifiedDataTablePluginApi", + "section": "def-public.RowControlColumn", + "text": "RowControlColumn" + }, + "[] | undefined" + ], + "path": "packages/kbn-unified-data-table/src/components/data_table.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/unified-data-table", "id": "def-public.UnifiedDataTableProps.totalHits", @@ -2093,23 +2332,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.UnifiedDataTableProps.trailingControlColumns", - "type": "Array", - "tags": [], - "label": "trailingControlColumns", - "description": [ - "\nAn optional list of the EuiDataGridControlColumn type for setting trailing control columns standard for EuiDataGrid." - ], - "signature": [ - "EuiDataGridControlColumn", - "[] | undefined" - ], - "path": "packages/kbn-unified-data-table/src/components/data_table.tsx", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "@kbn/unified-data-table", "id": "def-public.UnifiedDataTableProps.visibleCellActions", @@ -2203,29 +2425,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.UnifiedDataTableProps.customControlColumnsConfiguration", - "type": "Function", - "tags": [], - "label": "customControlColumnsConfiguration", - "description": [ - "\nAn optional settings to control which columns to render as trailing and leading control columns" - ], - "signature": [ - { - "pluginId": "@kbn/unified-data-table", - "scope": "public", - "docId": "kibKbnUnifiedDataTablePluginApi", - "section": "def-public.CustomControlColumnConfiguration", - "text": "CustomControlColumnConfiguration" - }, - " | undefined" - ], - "path": "packages/kbn-unified-data-table/src/components/data_table.tsx", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "@kbn/unified-data-table", "id": "def-public.UnifiedDataTableProps.consumer", @@ -2553,56 +2752,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.CustomControlColumnConfiguration", - "type": "Type", - "tags": [], - "label": "CustomControlColumnConfiguration", - "description": [], - "signature": [ - "(props: ", - { - "pluginId": "@kbn/unified-data-table", - "scope": "public", - "docId": "kibKbnUnifiedDataTablePluginApi", - "section": "def-public.ControlColumnsProps", - "text": "ControlColumnsProps" - }, - ") => { leadingControlColumns: ", - "EuiDataGridControlColumn", - "[]; trailingControlColumns?: ", - "EuiDataGridControlColumn", - "[] | undefined; }" - ], - "path": "packages/kbn-unified-data-table/src/types.ts", - "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "@kbn/unified-data-table", - "id": "def-public.CustomControlColumnConfiguration.$1", - "type": "Object", - "tags": [], - "label": "props", - "description": [], - "signature": [ - { - "pluginId": "@kbn/unified-data-table", - "scope": "public", - "docId": "kibKbnUnifiedDataTablePluginApi", - "section": "def-public.ControlColumnsProps", - "text": "ControlColumnsProps" - } - ], - "path": "packages/kbn-unified-data-table/src/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/unified-data-table", "id": "def-public.CustomGridColumnsConfiguration", @@ -2708,6 +2857,60 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlComponent", + "type": "Type", + "tags": [], + "label": "RowControlComponent", + "description": [], + "signature": [ + "React.FunctionComponent<", + { + "pluginId": "@kbn/unified-data-table", + "scope": "public", + "docId": "kibKbnUnifiedDataTablePluginApi", + "section": "def-public.RowControlProps", + "text": "RowControlProps" + }, + ">" + ], + "path": "packages/kbn-unified-data-table/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlComponent.$1", + "type": "CompoundType", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P & { children?: React.ReactNode; }" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/unified-data-table", + "id": "def-public.RowControlComponent.$2", + "type": "Any", + "tags": [], + "label": "context", + "description": [], + "signature": [ + "any" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/unified-data-table", "id": "def-public.SELECT_ROW", diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index c1cd06b499b0b..0d678dee656b2 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.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 | |-------------------|-----------|------------------------|-----------------| -| 154 | 0 | 81 | 1 | +| 166 | 0 | 92 | 2 | ## Client diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index 6d693048a795b..dd40ab84f750a 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.devdocs.json b/api_docs/kbn_unified_field_list.devdocs.json index 408aed745f51c..ccbed1bcf4edb 100644 --- a/api_docs/kbn_unified_field_list.devdocs.json +++ b/api_docs/kbn_unified_field_list.devdocs.json @@ -2203,7 +2203,7 @@ "section": "def-public.CoreStart", "text": "CoreStart" }, - ", \"uiSettings\" | \"analytics\">; data: ", + ", \"analytics\" | \"uiSettings\">; data: ", { "pluginId": "data", "scope": "public", diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 9d312b081274f..59912836c80d2 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-08-06 +date: 2024-08-09 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 987ed2e8d6b72..8093cd5714992 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_prompt.mdx b/api_docs/kbn_unsaved_changes_prompt.mdx index dc6b5e63a780e..f5b13b98e6dd1 100644 --- a/api_docs/kbn_unsaved_changes_prompt.mdx +++ b/api_docs/kbn_unsaved_changes_prompt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-prompt title: "@kbn/unsaved-changes-prompt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-prompt plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-prompt'] --- import kbnUnsavedChangesPromptObj from './kbn_unsaved_changes_prompt.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 03abfea96c404..aa9725cbfdc65 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-08-06 +date: 2024-08-09 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 92a1b8f5db94a..5f38a00aac58b 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2024-08-06 +date: 2024-08-09 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 079b028790785..cf4dd152185e4 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2024-08-06 +date: 2024-08-09 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 6f5c1c266b763..6911a2336aa89 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index b3f33d597250c..69395d6553f8f 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index b2b87eb221653..654a50048ed8c 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index 39297d15affbc..168f64dc4d557 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 41ed4bcd1c820..f5de7e0e3da0b 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 6da7cc7741656..4ce37322d2d25 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod.mdx b/api_docs/kbn_zod.mdx index ff19e694301c2..e5f422bc3fbcc 100644 --- a/api_docs/kbn_zod.mdx +++ b/api_docs/kbn_zod.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod title: "@kbn/zod" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod'] --- import kbnZodObj from './kbn_zod.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 6a9eaf9ffcc91..e7f46a8e5d907 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-08-06 +date: 2024-08-09 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 241759f565ad6..57ae1ecf77fdb 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index e749565f3711f..5482a932c3c9c 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index a33ce83c851bf..b575b25792a44 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 1b0fa82e3c5b3..368833dcf20b7 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index a35aff0e11fe3..8c9e398a47960 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index cfca2942708d9..9f92581905720 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index a3539148aa972..e4831463c9857 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 3e40fd7526707..f666e8ff207fc 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index e46f2f979b136..d9c77df8d9daa 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 494cab354ec1f..59b9402b2b316 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.devdocs.json b/api_docs/logs_data_access.devdocs.json index c695eedcacdbf..4098b0950c6f1 100644 --- a/api_docs/logs_data_access.devdocs.json +++ b/api_docs/logs_data_access.devdocs.json @@ -6,7 +6,41 @@ "interfaces": [], "enums": [], "misc": [], - "objects": [] + "objects": [], + "setup": { + "parentPluginId": "logsDataAccess", + "id": "def-public.LogsDataAccessPluginSetup", + "type": "Type", + "tags": [], + "label": "LogsDataAccessPluginSetup", + "description": [], + "signature": [ + "void" + ], + "path": "x-pack/plugins/observability_solution/logs_data_access/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "logsDataAccess", + "id": "def-public.LogsDataAccessPluginStart", + "type": "Type", + "tags": [], + "label": "LogsDataAccessPluginStart", + "description": [], + "signature": [ + "{ services: { logSourcesService: ", + "LogSourcesService", + "; }; }" + ], + "path": "x-pack/plugins/observability_solution/logs_data_access/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "lifecycle": "start", + "initialIsOpen": true + } }, "server": { "classes": [], @@ -132,7 +166,17 @@ "LogsErrorRateTimeseries", ") => Promise<", "LogsErrorRateTimeseriesReturnType", - ">; getLogSourcesService: (request: ", + ">; logSourcesServiceFactory: { getLogSourcesService(savedObjectsClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "server", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-server.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + "): Promise<", + "LogSourcesService", + ">; getScopedLogSourcesService(request: ", { "pluginId": "@kbn/core-http-server", "scope": "server", @@ -140,11 +184,9 @@ "section": "def-server.KibanaRequest", "text": "KibanaRequest" }, - ") => Promise<{ getLogSources: () => Promise<", - "LogSource", - "[]>; setLogSources: (sources: ", - "LogSource", - "[]) => Promise; }>; }; }" + "): Promise<", + "LogSourcesService", + ">; }; }; }" ], "path": "x-pack/plugins/observability_solution/logs_data_access/server/plugin.ts", "deprecated": false, diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 4d5ace7813971..3c859a51fc723 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; @@ -21,7 +21,15 @@ Contact [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 7 | 0 | 7 | 6 | +| 9 | 0 | 9 | 6 | + +## Client + +### Setup + + +### Start + ## Server diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index f9a8fe825b59f..df1ba1c191a82 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.devdocs.json b/api_docs/logs_shared.devdocs.json index d0c2a280e1eab..32758ecf4558d 100644 --- a/api_docs/logs_shared.devdocs.json +++ b/api_docs/logs_shared.devdocs.json @@ -11,7 +11,7 @@ "label": "getLogViewReferenceFromUrl", "description": [], "signature": [ - "({ logViewKey, sourceIdKey, toastsService, urlStateStorage, }: LogViewUrlStateDependencies) => { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } | null" + "({ logViewKey, sourceIdKey, toastsService, urlStateStorage, }: LogViewUrlStateDependencies) => { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } | null" ], "path": "x-pack/plugins/observability_solution/logs_shared/public/observability_logs/log_view_state/src/url_state_storage_service.ts", "deprecated": false, @@ -840,7 +840,7 @@ "label": "LogViewProvider", "description": [], "signature": [ - "React.FunctionComponent; hasFailedLoading: boolean; hasFailedLoadingLogView: boolean; hasFailedLoadingLogViewStatus: boolean; hasFailedResolvingLogView: boolean; latestLoadLogViewFailures: Error[]; isUninitialized: boolean; isLoading: boolean; isLoadingLogView: boolean; isLoadingLogViewStatus: boolean; isResolvingLogView: boolean; logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }; logView: ({ id: string; origin: \"internal\" | \"inline\" | \"stored\" | \"infra-source-stored\" | \"infra-source-internal\" | \"infra-source-fallback\"; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } & { updatedAt?: number | undefined; version?: string | undefined; }) | undefined; resolvedLogView: ", + ">; hasFailedLoading: boolean; hasFailedLoadingLogView: boolean; hasFailedLoadingLogViewStatus: boolean; hasFailedResolvingLogView: boolean; latestLoadLogViewFailures: Error[]; isUninitialized: boolean; isLoading: boolean; isLoadingLogView: boolean; isLoadingLogViewStatus: boolean; isResolvingLogView: boolean; logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }; logView: ({ id: string; origin: \"internal\" | \"inline\" | \"stored\" | \"infra-source-stored\" | \"infra-source-internal\" | \"infra-source-fallback\"; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } & { updatedAt?: number | undefined; version?: string | undefined; }) | undefined; resolvedLogView: ", { "pluginId": "logsShared", "scope": "common", @@ -1918,7 +1918,7 @@ "BaseActionObject", ", ", "ServiceMap", - ">>; update: (logViewAttributes: Partial<{ name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }>) => Promise; changeLogViewReference: (logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }) => void; revertToDefaultLogView: () => void; }" + ">>; update: (logViewAttributes: Partial<{ name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }>) => Promise; changeLogViewReference: (logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }) => void; revertToDefaultLogView: () => void; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/public/hooks/use_log_view.ts", "deprecated": false, @@ -1943,7 +1943,7 @@ "label": "initialLogViewReference", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } | undefined" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } | undefined" ], "path": "x-pack/plugins/observability_solution/logs_shared/public/hooks/use_log_view.ts", "deprecated": false, @@ -2343,7 +2343,7 @@ "section": "def-public.LogViewNotificationEvent", "text": "LogViewNotificationEvent" }, - ">; hasFailedLoading: boolean; hasFailedLoadingLogView: boolean; hasFailedLoadingLogViewStatus: boolean; hasFailedResolvingLogView: boolean; latestLoadLogViewFailures: Error[]; isUninitialized: boolean; isLoading: boolean; isLoadingLogView: boolean; isLoadingLogViewStatus: boolean; isResolvingLogView: boolean; logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }; logView: ({ id: string; origin: \"internal\" | \"inline\" | \"stored\" | \"infra-source-stored\" | \"infra-source-internal\" | \"infra-source-fallback\"; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } & { updatedAt?: number | undefined; version?: string | undefined; }) | undefined; resolvedLogView: ", + ">; hasFailedLoading: boolean; hasFailedLoadingLogView: boolean; hasFailedLoadingLogViewStatus: boolean; hasFailedResolvingLogView: boolean; latestLoadLogViewFailures: Error[]; isUninitialized: boolean; isLoading: boolean; isLoadingLogView: boolean; isLoadingLogViewStatus: boolean; isResolvingLogView: boolean; logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }; logView: ({ id: string; origin: \"internal\" | \"inline\" | \"stored\" | \"infra-source-stored\" | \"infra-source-internal\" | \"infra-source-fallback\"; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } & { updatedAt?: number | undefined; version?: string | undefined; }) | undefined; resolvedLogView: ", { "pluginId": "logsShared", "scope": "common", @@ -2499,7 +2499,7 @@ "BaseActionObject", ", ", "ServiceMap", - ">>; update: (logViewAttributes: Partial<{ name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }>) => Promise; changeLogViewReference: (logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }) => void; revertToDefaultLogView: () => void; }" + ">>; update: (logViewAttributes: Partial<{ name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }>) => Promise; changeLogViewReference: (logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }) => void; revertToDefaultLogView: () => void; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/public/hooks/use_log_view.ts", "deprecated": false, @@ -2905,6 +2905,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "logsShared", + "id": "def-public.LogsSharedClientStartDeps.logsDataAccess", + "type": "Object", + "tags": [], + "label": "logsDataAccess", + "description": [], + "signature": [ + "{ services: { logSourcesService: ", + "LogSourcesService", + "; }; }" + ], + "path": "x-pack/plugins/observability_solution/logs_shared/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "logsShared", "id": "def-public.LogsSharedClientStartDeps.observabilityAIAssistant", @@ -3345,7 +3361,7 @@ "label": "LogViewNotificationEvent", "description": [], "signature": [ - "{ type: \"LOADING_LOG_VIEW_STARTED\"; logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }; } | { type: \"LOADING_LOG_VIEW_SUCCEEDED\"; resolvedLogView: ", + "{ type: \"LOADING_LOG_VIEW_STARTED\"; logViewReference: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }; } | { type: \"LOADING_LOG_VIEW_SUCCEEDED\"; resolvedLogView: ", { "pluginId": "logsShared", "scope": "common", @@ -3578,7 +3594,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", "LogEntriesAroundParams", ", columnOverrides?: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[] | undefined) => Promise<{ entries: { id: string; index: string; cursor: { time: string; tiebreaker: number; }; columns: ({ columnId: string; time: string; } | { columnId: string; field: string; value: ", { @@ -3631,7 +3647,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -3687,7 +3703,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", "LogEntriesParams", ", columnOverrides?: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[] | undefined) => Promise<{ entries: { id: string; index: string; cursor: { time: string; tiebreaker: number; }; columns: ({ columnId: string; time: string; } | { columnId: string; field: string; value: ", { @@ -3740,7 +3756,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -3796,7 +3812,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, start: number, end: number, bucketSize: number, filterQuery?: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, start: number, end: number, bucketSize: number, filterQuery?: ", { "pluginId": "@kbn/utility-types", "scope": "common", @@ -3839,7 +3855,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -3932,7 +3948,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, startTimestamp: number, endTimestamp: number, bucketSize: number, highlightQueries: string[], filterQuery?: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, startTimestamp: number, endTimestamp: number, bucketSize: number, highlightQueries: string[], filterQuery?: ", { "pluginId": "@kbn/utility-types", "scope": "common", @@ -3975,7 +3991,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -4223,7 +4239,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", "LogEntriesAroundParams", ", columnOverrides?: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[] | undefined) => Promise<{ entries: { id: string; index: string; cursor: { time: string; tiebreaker: number; }; columns: ({ columnId: string; time: string; } | { columnId: string; field: string; value: ", { @@ -4276,7 +4292,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -4332,7 +4348,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, params: ", "LogEntriesParams", ", columnOverrides?: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[] | undefined) => Promise<{ entries: { id: string; index: string; cursor: { time: string; tiebreaker: number; }; columns: ({ columnId: string; time: string; } | { columnId: string; field: string; value: ", { @@ -4385,7 +4401,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -4441,7 +4457,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, start: number, end: number, bucketSize: number, filterQuery?: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, start: number, end: number, bucketSize: number, filterQuery?: ", { "pluginId": "@kbn/utility-types", "scope": "common", @@ -4484,7 +4500,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -4577,7 +4593,7 @@ "section": "def-server.RequestHandlerContext", "text": "RequestHandlerContext" }, - ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, startTimestamp: number, endTimestamp: number, bucketSize: number, highlightQueries: string[], filterQuery?: ", + ", logView: { logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }, startTimestamp: number, endTimestamp: number, bucketSize: number, highlightQueries: string[], filterQuery?: ", { "pluginId": "@kbn/utility-types", "scope": "common", @@ -4620,7 +4636,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts", "deprecated": false, @@ -5476,7 +5492,7 @@ "label": "logView", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } | undefined" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } | undefined" ], "path": "x-pack/plugins/observability_solution/logs_shared/common/locators/types.ts", "deprecated": false, @@ -6093,7 +6109,7 @@ "label": "LogIndexReference", "description": [], "signature": [ - "{ type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }" + "{ type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts", "deprecated": false, @@ -6199,6 +6215,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "logsShared", + "id": "def-common.LogSourcesKibanaAdvancedSettingReference", + "type": "Type", + "tags": [], + "label": "LogSourcesKibanaAdvancedSettingReference", + "description": [], + "signature": [ + "{ type: \"kibana_advanced_setting\"; }" + ], + "path": "x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "logsShared", "id": "def-common.LogTimestampColumn", @@ -6222,7 +6253,7 @@ "label": "LogView", "description": [], "signature": [ - "{ id: string; origin: \"internal\" | \"inline\" | \"stored\" | \"infra-source-stored\" | \"infra-source-internal\" | \"infra-source-fallback\"; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } & { updatedAt?: number | undefined; version?: string | undefined; }" + "{ id: string; origin: \"internal\" | \"inline\" | \"stored\" | \"infra-source-stored\" | \"infra-source-internal\" | \"infra-source-fallback\"; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; } & { updatedAt?: number | undefined; version?: string | undefined; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts", "deprecated": false, @@ -6237,7 +6268,7 @@ "label": "LogViewAttributes", "description": [], "signature": [ - "{ name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }" + "{ name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts", "deprecated": false, @@ -6267,7 +6298,7 @@ "label": "LogViewReference", "description": [], "signature": [ - "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" + "{ logViewId: string; type: \"log-view-reference\"; } | { type: \"log-view-inline\"; id: string; attributes: { name: string; description: string; logIndices: { type: \"data_view\"; dataViewId: string; } | { type: \"index_name\"; indexName: string; } | { type: \"kibana_advanced_setting\"; }; logColumns: ({ timestampColumn: { id: string; }; } | { messageColumn: { id: string; }; } | { fieldColumn: { id: string; } & { field: string; }; })[]; }; }" ], "path": "x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts", "deprecated": false, @@ -6704,7 +6735,11 @@ "LiteralC", "<\"index_name\">; indexName: ", "StringC", - "; }>]>; logColumns: ", + "; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"kibana_advanced_setting\">; }>]>; logColumns: ", "ArrayC", "<", "UnionC", @@ -6848,7 +6883,11 @@ "LiteralC", "<\"index_name\">; indexName: ", "StringC", - "; }>]>; logColumns: ", + "; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"kibana_advanced_setting\">; }>]>; logColumns: ", "ArrayC", "<", "UnionC", @@ -7004,7 +7043,11 @@ "LiteralC", "<\"index_name\">; indexName: ", "StringC", - "; }>]>; logColumns: ", + "; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"kibana_advanced_setting\">; }>]>; logColumns: ", "ArrayC", "<", "UnionC", @@ -7160,7 +7203,11 @@ "LiteralC", "<\"index_name\">; indexName: ", "StringC", - "; }>]>; logColumns: ", + "; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"kibana_advanced_setting\">; }>]>; logColumns: ", "ArrayC", "<", "UnionC", @@ -7570,7 +7617,11 @@ "LiteralC", "<\"index_name\">; indexName: ", "StringC", - "; }>]>; logColumns: ", + "; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"kibana_advanced_setting\">; }>]>; logColumns: ", "ArrayC", "<", "UnionC", @@ -8163,6 +8214,24 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "logsShared", + "id": "def-common.logSourcesKibanaAdvancedSettingRT", + "type": "Object", + "tags": [], + "label": "logSourcesKibanaAdvancedSettingRT", + "description": [], + "signature": [ + "TypeC", + "<{ type: ", + "LiteralC", + "<\"kibana_advanced_setting\">; }>" + ], + "path": "x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "logsShared", "id": "def-common.logTimestampColumnRT", @@ -8282,7 +8351,11 @@ "LiteralC", "<\"index_name\">; indexName: ", "StringC", - "; }>]>; logColumns: ", + "; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"kibana_advanced_setting\">; }>]>; logColumns: ", "ArrayC", "<", "UnionC", diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 0ba9d0cd6a01e..21900bf317223 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.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 | |-------------------|-----------|------------------------|-----------------| -| 297 | 0 | 269 | 32 | +| 300 | 0 | 272 | 32 | ## Client diff --git a/api_docs/management.mdx b/api_docs/management.mdx index f8b04bfe3afaa..0c26d51706f47 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2024-08-06 +date: 2024-08-09 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 38cad5cf4eaa3..3521c63bfd0c5 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2024-08-06 +date: 2024-08-09 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 8820fac97213f..debc3f409b82d 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.devdocs.json b/api_docs/metrics_data_access.devdocs.json index 19c353e045c3d..786c41c318074 100644 --- a/api_docs/metrics_data_access.devdocs.json +++ b/api_docs/metrics_data_access.devdocs.json @@ -745,7 +745,7 @@ "label": "hostSnapshotMetricTypes", "description": [], "signature": [ - "(\"memory\" | \"rx\" | \"normalizedLoad1m\" | \"memoryFree\" | \"tx\" | \"logRate\" | \"cpu\" | \"diskLatency\" | \"diskSpaceUsage\" | \"load\" | \"memoryTotal\" | \"rxV2\" | \"txV2\")[]" + "(\"memory\" | \"logRate\" | \"rx\" | \"normalizedLoad1m\" | \"memoryFree\" | \"tx\" | \"cpu\" | \"cpuTotal\" | \"diskLatency\" | \"diskSpaceUsage\" | \"load\" | \"memoryTotal\" | \"rxV2\" | \"txV2\")[]" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts", "deprecated": false, @@ -790,7 +790,7 @@ "label": "InventoryMetric", "description": [], "signature": [ - "\"custom\" | \"hostSystemOverview\" | \"hostCpuUsage\" | \"hostFilesystem\" | \"hostK8sOverview\" | \"hostK8sCpuCap\" | \"hostK8sDiskCap\" | \"hostK8sMemoryCap\" | \"hostK8sPodCap\" | \"hostLoad\" | \"hostMemoryUsage\" | \"hostNetworkTraffic\" | \"hostDockerOverview\" | \"hostDockerInfo\" | \"hostDockerTop5ByCpu\" | \"hostDockerTop5ByMemory\" | \"podOverview\" | \"podCpuUsage\" | \"podMemoryUsage\" | \"podLogUsage\" | \"podNetworkTraffic\" | \"containerOverview\" | \"containerCpuKernel\" | \"containerCpuUsage\" | \"containerDiskIOOps\" | \"containerDiskIOBytes\" | \"containerMemory\" | \"containerNetworkTraffic\" | \"containerK8sOverview\" | \"containerK8sCpuUsage\" | \"containerK8sMemoryUsage\" | \"nginxHits\" | \"nginxRequestRate\" | \"nginxActiveConnections\" | \"nginxRequestsPerConnection\" | \"awsOverview\" | \"awsCpuUtilization\" | \"awsNetworkBytes\" | \"awsNetworkPackets\" | \"awsDiskioBytes\" | \"awsDiskioOps\" | \"awsEC2CpuUtilization\" | \"awsEC2NetworkTraffic\" | \"awsEC2DiskIOBytes\" | \"awsS3TotalRequests\" | \"awsS3NumberOfObjects\" | \"awsS3BucketSize\" | \"awsS3DownloadBytes\" | \"awsS3UploadBytes\" | \"awsRDSCpuTotal\" | \"awsRDSConnections\" | \"awsRDSQueriesExecuted\" | \"awsRDSActiveTransactions\" | \"awsRDSLatency\" | \"awsSQSMessagesVisible\" | \"awsSQSMessagesDelayed\" | \"awsSQSMessagesSent\" | \"awsSQSMessagesEmpty\" | \"awsSQSOldestMessage\"" + "\"custom\" | \"hostSystemOverview\" | \"hostCpuUsageTotal\" | \"hostCpuUsage\" | \"hostFilesystem\" | \"hostK8sOverview\" | \"hostK8sCpuCap\" | \"hostK8sDiskCap\" | \"hostK8sMemoryCap\" | \"hostK8sPodCap\" | \"hostLoad\" | \"hostMemoryUsage\" | \"hostNetworkTraffic\" | \"hostDockerOverview\" | \"hostDockerInfo\" | \"hostDockerTop5ByCpu\" | \"hostDockerTop5ByMemory\" | \"podOverview\" | \"podCpuUsage\" | \"podMemoryUsage\" | \"podLogUsage\" | \"podNetworkTraffic\" | \"containerOverview\" | \"containerCpuKernel\" | \"containerCpuUsage\" | \"containerDiskIOOps\" | \"containerDiskIOBytes\" | \"containerMemory\" | \"containerNetworkTraffic\" | \"containerK8sOverview\" | \"containerK8sCpuUsage\" | \"containerK8sMemoryUsage\" | \"nginxHits\" | \"nginxRequestRate\" | \"nginxActiveConnections\" | \"nginxRequestsPerConnection\" | \"awsOverview\" | \"awsCpuUtilization\" | \"awsNetworkBytes\" | \"awsNetworkPackets\" | \"awsDiskioBytes\" | \"awsDiskioOps\" | \"awsEC2CpuUtilization\" | \"awsEC2NetworkTraffic\" | \"awsEC2DiskIOBytes\" | \"awsS3TotalRequests\" | \"awsS3NumberOfObjects\" | \"awsS3BucketSize\" | \"awsS3DownloadBytes\" | \"awsS3UploadBytes\" | \"awsRDSCpuTotal\" | \"awsRDSConnections\" | \"awsRDSQueriesExecuted\" | \"awsRDSActiveTransactions\" | \"awsRDSLatency\" | \"awsSQSMessagesVisible\" | \"awsSQSMessagesDelayed\" | \"awsSQSMessagesSent\" | \"awsSQSMessagesEmpty\" | \"awsSQSOldestMessage\"" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts", "deprecated": false, @@ -1522,7 +1522,7 @@ "label": "SnapshotMetricType", "description": [], "signature": [ - "\"count\" | \"memory\" | \"custom\" | \"rx\" | \"normalizedLoad1m\" | \"memoryFree\" | \"tx\" | \"logRate\" | \"cpu\" | \"s3BucketSize\" | \"s3NumberOfObjects\" | \"s3TotalRequests\" | \"s3UploadBytes\" | \"s3DownloadBytes\" | \"diskLatency\" | \"diskSpaceUsage\" | \"load\" | \"memoryTotal\" | \"rxV2\" | \"txV2\" | \"diskIOReadBytes\" | \"diskIOWriteBytes\" | \"rdsLatency\" | \"rdsConnections\" | \"rdsQueriesExecuted\" | \"rdsActiveTransactions\" | \"sqsMessagesVisible\" | \"sqsMessagesDelayed\" | \"sqsMessagesEmpty\" | \"sqsMessagesSent\" | \"sqsOldestMessage\"" + "\"count\" | \"memory\" | \"custom\" | \"logRate\" | \"rx\" | \"normalizedLoad1m\" | \"memoryFree\" | \"tx\" | \"cpu\" | \"s3BucketSize\" | \"s3NumberOfObjects\" | \"s3TotalRequests\" | \"s3UploadBytes\" | \"s3DownloadBytes\" | \"cpuTotal\" | \"diskLatency\" | \"diskSpaceUsage\" | \"load\" | \"memoryTotal\" | \"rxV2\" | \"txV2\" | \"diskIOReadBytes\" | \"diskIOWriteBytes\" | \"rdsLatency\" | \"rdsConnections\" | \"rdsQueriesExecuted\" | \"rdsActiveTransactions\" | \"sqsMessagesVisible\" | \"sqsMessagesDelayed\" | \"sqsMessagesEmpty\" | \"sqsMessagesSent\" | \"sqsOldestMessage\"" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts", "deprecated": false, @@ -1537,7 +1537,7 @@ "label": "TSVBMetricModel", "description": [], "signature": [ - "{ id: \"custom\" | \"hostSystemOverview\" | \"hostCpuUsage\" | \"hostFilesystem\" | \"hostK8sOverview\" | \"hostK8sCpuCap\" | \"hostK8sDiskCap\" | \"hostK8sMemoryCap\" | \"hostK8sPodCap\" | \"hostLoad\" | \"hostMemoryUsage\" | \"hostNetworkTraffic\" | \"hostDockerOverview\" | \"hostDockerInfo\" | \"hostDockerTop5ByCpu\" | \"hostDockerTop5ByMemory\" | \"podOverview\" | \"podCpuUsage\" | \"podMemoryUsage\" | \"podLogUsage\" | \"podNetworkTraffic\" | \"containerOverview\" | \"containerCpuKernel\" | \"containerCpuUsage\" | \"containerDiskIOOps\" | \"containerDiskIOBytes\" | \"containerMemory\" | \"containerNetworkTraffic\" | \"containerK8sOverview\" | \"containerK8sCpuUsage\" | \"containerK8sMemoryUsage\" | \"nginxHits\" | \"nginxRequestRate\" | \"nginxActiveConnections\" | \"nginxRequestsPerConnection\" | \"awsOverview\" | \"awsCpuUtilization\" | \"awsNetworkBytes\" | \"awsNetworkPackets\" | \"awsDiskioBytes\" | \"awsDiskioOps\" | \"awsEC2CpuUtilization\" | \"awsEC2NetworkTraffic\" | \"awsEC2DiskIOBytes\" | \"awsS3TotalRequests\" | \"awsS3NumberOfObjects\" | \"awsS3BucketSize\" | \"awsS3DownloadBytes\" | \"awsS3UploadBytes\" | \"awsRDSCpuTotal\" | \"awsRDSConnections\" | \"awsRDSQueriesExecuted\" | \"awsRDSActiveTransactions\" | \"awsRDSLatency\" | \"awsSQSMessagesVisible\" | \"awsSQSMessagesDelayed\" | \"awsSQSMessagesSent\" | \"awsSQSMessagesEmpty\" | \"awsSQSOldestMessage\"; requires: string[]; index_pattern: string | string[]; interval: string; time_field: string; type: string; series: ({ id: string; metrics: ({ id: string; type: \"count\"; } | ({ id: string; type: \"min\" | \"max\" | \"sum\" | \"avg\" | \"count\" | \"cumulative_sum\" | \"derivative\" | \"cardinality\" | \"calculation\" | \"series_agg\" | \"positive_only\"; } & { field?: string | undefined; }) | { id: string; script: string; type: \"calculation\"; variables: { field: string; id: string; name: string; }[]; } | { id: string; field: string; unit: string; type: \"derivative\"; } | ({ id: string; type: \"percentile\"; percentiles: { id: string; value: number; }[]; } & { field?: string | undefined; }) | { id: string; function: string; type: \"series_agg\"; })[]; split_mode: string; } & { terms_field?: string | undefined; terms_size?: number | undefined; terms_order_by?: string | undefined; filter?: { query: string; language: \"kuery\" | \"lucene\"; } | undefined; })[]; } & { filter?: string | undefined; map_field_to?: string | undefined; id_type?: \"cloud\" | \"node\" | undefined; drop_last_bucket?: boolean | undefined; }" + "{ id: \"custom\" | \"hostSystemOverview\" | \"hostCpuUsageTotal\" | \"hostCpuUsage\" | \"hostFilesystem\" | \"hostK8sOverview\" | \"hostK8sCpuCap\" | \"hostK8sDiskCap\" | \"hostK8sMemoryCap\" | \"hostK8sPodCap\" | \"hostLoad\" | \"hostMemoryUsage\" | \"hostNetworkTraffic\" | \"hostDockerOverview\" | \"hostDockerInfo\" | \"hostDockerTop5ByCpu\" | \"hostDockerTop5ByMemory\" | \"podOverview\" | \"podCpuUsage\" | \"podMemoryUsage\" | \"podLogUsage\" | \"podNetworkTraffic\" | \"containerOverview\" | \"containerCpuKernel\" | \"containerCpuUsage\" | \"containerDiskIOOps\" | \"containerDiskIOBytes\" | \"containerMemory\" | \"containerNetworkTraffic\" | \"containerK8sOverview\" | \"containerK8sCpuUsage\" | \"containerK8sMemoryUsage\" | \"nginxHits\" | \"nginxRequestRate\" | \"nginxActiveConnections\" | \"nginxRequestsPerConnection\" | \"awsOverview\" | \"awsCpuUtilization\" | \"awsNetworkBytes\" | \"awsNetworkPackets\" | \"awsDiskioBytes\" | \"awsDiskioOps\" | \"awsEC2CpuUtilization\" | \"awsEC2NetworkTraffic\" | \"awsEC2DiskIOBytes\" | \"awsS3TotalRequests\" | \"awsS3NumberOfObjects\" | \"awsS3BucketSize\" | \"awsS3DownloadBytes\" | \"awsS3UploadBytes\" | \"awsRDSCpuTotal\" | \"awsRDSConnections\" | \"awsRDSQueriesExecuted\" | \"awsRDSActiveTransactions\" | \"awsRDSLatency\" | \"awsSQSMessagesVisible\" | \"awsSQSMessagesDelayed\" | \"awsSQSMessagesSent\" | \"awsSQSMessagesEmpty\" | \"awsSQSOldestMessage\"; requires: string[]; index_pattern: string | string[]; interval: string; time_field: string; type: string; series: ({ id: string; metrics: ({ id: string; type: \"count\"; } | ({ id: string; type: \"min\" | \"max\" | \"sum\" | \"avg\" | \"count\" | \"cardinality\" | \"cumulative_sum\" | \"derivative\" | \"calculation\" | \"series_agg\" | \"positive_only\"; } & { field?: string | undefined; }) | { id: string; script: string; type: \"calculation\"; variables: { field: string; id: string; name: string; }[]; } | { id: string; field: string; unit: string; type: \"derivative\"; } | ({ id: string; type: \"percentile\"; percentiles: { id: string; value: number; }[]; } & { field?: string | undefined; }) | { id: string; function: string; type: \"series_agg\"; })[]; split_mode: string; } & { terms_field?: string | undefined; terms_size?: number | undefined; terms_order_by?: string | undefined; filter?: { query: string; language: \"kuery\" | \"lucene\"; } | undefined; })[]; } & { filter?: string | undefined; map_field_to?: string | undefined; id_type?: \"cloud\" | \"node\" | undefined; drop_last_bucket?: boolean | undefined; }" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts", "deprecated": false, @@ -1552,7 +1552,7 @@ "label": "TSVBMetricModelCreator", "description": [], "signature": [ - "(timeField: string, indexPattern: string | string[], interval: string) => { id: \"custom\" | \"hostSystemOverview\" | \"hostCpuUsage\" | \"hostFilesystem\" | \"hostK8sOverview\" | \"hostK8sCpuCap\" | \"hostK8sDiskCap\" | \"hostK8sMemoryCap\" | \"hostK8sPodCap\" | \"hostLoad\" | \"hostMemoryUsage\" | \"hostNetworkTraffic\" | \"hostDockerOverview\" | \"hostDockerInfo\" | \"hostDockerTop5ByCpu\" | \"hostDockerTop5ByMemory\" | \"podOverview\" | \"podCpuUsage\" | \"podMemoryUsage\" | \"podLogUsage\" | \"podNetworkTraffic\" | \"containerOverview\" | \"containerCpuKernel\" | \"containerCpuUsage\" | \"containerDiskIOOps\" | \"containerDiskIOBytes\" | \"containerMemory\" | \"containerNetworkTraffic\" | \"containerK8sOverview\" | \"containerK8sCpuUsage\" | \"containerK8sMemoryUsage\" | \"nginxHits\" | \"nginxRequestRate\" | \"nginxActiveConnections\" | \"nginxRequestsPerConnection\" | \"awsOverview\" | \"awsCpuUtilization\" | \"awsNetworkBytes\" | \"awsNetworkPackets\" | \"awsDiskioBytes\" | \"awsDiskioOps\" | \"awsEC2CpuUtilization\" | \"awsEC2NetworkTraffic\" | \"awsEC2DiskIOBytes\" | \"awsS3TotalRequests\" | \"awsS3NumberOfObjects\" | \"awsS3BucketSize\" | \"awsS3DownloadBytes\" | \"awsS3UploadBytes\" | \"awsRDSCpuTotal\" | \"awsRDSConnections\" | \"awsRDSQueriesExecuted\" | \"awsRDSActiveTransactions\" | \"awsRDSLatency\" | \"awsSQSMessagesVisible\" | \"awsSQSMessagesDelayed\" | \"awsSQSMessagesSent\" | \"awsSQSMessagesEmpty\" | \"awsSQSOldestMessage\"; requires: string[]; index_pattern: string | string[]; interval: string; time_field: string; type: string; series: ({ id: string; metrics: ({ id: string; type: \"count\"; } | ({ id: string; type: \"min\" | \"max\" | \"sum\" | \"avg\" | \"count\" | \"cumulative_sum\" | \"derivative\" | \"cardinality\" | \"calculation\" | \"series_agg\" | \"positive_only\"; } & { field?: string | undefined; }) | { id: string; script: string; type: \"calculation\"; variables: { field: string; id: string; name: string; }[]; } | { id: string; field: string; unit: string; type: \"derivative\"; } | ({ id: string; type: \"percentile\"; percentiles: { id: string; value: number; }[]; } & { field?: string | undefined; }) | { id: string; function: string; type: \"series_agg\"; })[]; split_mode: string; } & { terms_field?: string | undefined; terms_size?: number | undefined; terms_order_by?: string | undefined; filter?: { query: string; language: \"kuery\" | \"lucene\"; } | undefined; })[]; } & { filter?: string | undefined; map_field_to?: string | undefined; id_type?: \"cloud\" | \"node\" | undefined; drop_last_bucket?: boolean | undefined; }" + "(timeField: string, indexPattern: string | string[], interval: string) => { id: \"custom\" | \"hostSystemOverview\" | \"hostCpuUsageTotal\" | \"hostCpuUsage\" | \"hostFilesystem\" | \"hostK8sOverview\" | \"hostK8sCpuCap\" | \"hostK8sDiskCap\" | \"hostK8sMemoryCap\" | \"hostK8sPodCap\" | \"hostLoad\" | \"hostMemoryUsage\" | \"hostNetworkTraffic\" | \"hostDockerOverview\" | \"hostDockerInfo\" | \"hostDockerTop5ByCpu\" | \"hostDockerTop5ByMemory\" | \"podOverview\" | \"podCpuUsage\" | \"podMemoryUsage\" | \"podLogUsage\" | \"podNetworkTraffic\" | \"containerOverview\" | \"containerCpuKernel\" | \"containerCpuUsage\" | \"containerDiskIOOps\" | \"containerDiskIOBytes\" | \"containerMemory\" | \"containerNetworkTraffic\" | \"containerK8sOverview\" | \"containerK8sCpuUsage\" | \"containerK8sMemoryUsage\" | \"nginxHits\" | \"nginxRequestRate\" | \"nginxActiveConnections\" | \"nginxRequestsPerConnection\" | \"awsOverview\" | \"awsCpuUtilization\" | \"awsNetworkBytes\" | \"awsNetworkPackets\" | \"awsDiskioBytes\" | \"awsDiskioOps\" | \"awsEC2CpuUtilization\" | \"awsEC2NetworkTraffic\" | \"awsEC2DiskIOBytes\" | \"awsS3TotalRequests\" | \"awsS3NumberOfObjects\" | \"awsS3BucketSize\" | \"awsS3DownloadBytes\" | \"awsS3UploadBytes\" | \"awsRDSCpuTotal\" | \"awsRDSConnections\" | \"awsRDSQueriesExecuted\" | \"awsRDSActiveTransactions\" | \"awsRDSLatency\" | \"awsSQSMessagesVisible\" | \"awsSQSMessagesDelayed\" | \"awsSQSMessagesSent\" | \"awsSQSMessagesEmpty\" | \"awsSQSOldestMessage\"; requires: string[]; index_pattern: string | string[]; interval: string; time_field: string; type: string; series: ({ id: string; metrics: ({ id: string; type: \"count\"; } | ({ id: string; type: \"min\" | \"max\" | \"sum\" | \"avg\" | \"count\" | \"cardinality\" | \"cumulative_sum\" | \"derivative\" | \"calculation\" | \"series_agg\" | \"positive_only\"; } & { field?: string | undefined; }) | { id: string; script: string; type: \"calculation\"; variables: { field: string; id: string; name: string; }[]; } | { id: string; field: string; unit: string; type: \"derivative\"; } | ({ id: string; type: \"percentile\"; percentiles: { id: string; value: number; }[]; } & { field?: string | undefined; }) | { id: string; function: string; type: \"series_agg\"; })[]; split_mode: string; } & { terms_field?: string | undefined; terms_size?: number | undefined; terms_order_by?: string | undefined; filter?: { query: string; language: \"kuery\" | \"lucene\"; } | undefined; })[]; } & { filter?: string | undefined; map_field_to?: string | undefined; id_type?: \"cloud\" | \"node\" | undefined; drop_last_bucket?: boolean | undefined; }" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts", "deprecated": false, @@ -1715,7 +1715,7 @@ "description": [], "signature": [ "KeyofC", - "<{ hostSystemOverview: null; hostCpuUsage: null; hostFilesystem: null; hostK8sOverview: null; hostK8sCpuCap: null; hostK8sDiskCap: null; hostK8sMemoryCap: null; hostK8sPodCap: null; hostLoad: null; hostMemoryUsage: null; hostNetworkTraffic: null; hostDockerOverview: null; hostDockerInfo: null; hostDockerTop5ByCpu: null; hostDockerTop5ByMemory: null; podOverview: null; podCpuUsage: null; podMemoryUsage: null; podLogUsage: null; podNetworkTraffic: null; containerOverview: null; containerCpuKernel: null; containerCpuUsage: null; containerDiskIOOps: null; containerDiskIOBytes: null; containerMemory: null; containerNetworkTraffic: null; containerK8sOverview: null; containerK8sCpuUsage: null; containerK8sMemoryUsage: null; nginxHits: null; nginxRequestRate: null; nginxActiveConnections: null; nginxRequestsPerConnection: null; awsOverview: null; awsCpuUtilization: null; awsNetworkBytes: null; awsNetworkPackets: null; awsDiskioBytes: null; awsDiskioOps: null; awsEC2CpuUtilization: null; awsEC2NetworkTraffic: null; awsEC2DiskIOBytes: null; awsS3TotalRequests: null; awsS3NumberOfObjects: null; awsS3BucketSize: null; awsS3DownloadBytes: null; awsS3UploadBytes: null; awsRDSCpuTotal: null; awsRDSConnections: null; awsRDSQueriesExecuted: null; awsRDSActiveTransactions: null; awsRDSLatency: null; awsSQSMessagesVisible: null; awsSQSMessagesDelayed: null; awsSQSMessagesSent: null; awsSQSMessagesEmpty: null; awsSQSOldestMessage: null; custom: null; }>" + "<{ hostSystemOverview: null; hostCpuUsageTotal: null; hostCpuUsage: null; hostFilesystem: null; hostK8sOverview: null; hostK8sCpuCap: null; hostK8sDiskCap: null; hostK8sMemoryCap: null; hostK8sPodCap: null; hostLoad: null; hostMemoryUsage: null; hostNetworkTraffic: null; hostDockerOverview: null; hostDockerInfo: null; hostDockerTop5ByCpu: null; hostDockerTop5ByMemory: null; podOverview: null; podCpuUsage: null; podMemoryUsage: null; podLogUsage: null; podNetworkTraffic: null; containerOverview: null; containerCpuKernel: null; containerCpuUsage: null; containerDiskIOOps: null; containerDiskIOBytes: null; containerMemory: null; containerNetworkTraffic: null; containerK8sOverview: null; containerK8sCpuUsage: null; containerK8sMemoryUsage: null; nginxHits: null; nginxRequestRate: null; nginxActiveConnections: null; nginxRequestsPerConnection: null; awsOverview: null; awsCpuUtilization: null; awsNetworkBytes: null; awsNetworkPackets: null; awsDiskioBytes: null; awsDiskioOps: null; awsEC2CpuUtilization: null; awsEC2NetworkTraffic: null; awsEC2DiskIOBytes: null; awsS3TotalRequests: null; awsS3NumberOfObjects: null; awsS3BucketSize: null; awsS3DownloadBytes: null; awsS3UploadBytes: null; awsRDSCpuTotal: null; awsRDSConnections: null; awsRDSQueriesExecuted: null; awsRDSActiveTransactions: null; awsRDSLatency: null; awsSQSMessagesVisible: null; awsSQSMessagesDelayed: null; awsSQSMessagesSent: null; awsSQSMessagesEmpty: null; awsSQSOldestMessage: null; custom: null; }>" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts", "deprecated": false, @@ -2064,6 +2064,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "metricsDataAccess", + "id": "def-common.SnapshotMetricTypeKeys.cpuTotal", + "type": "Uncategorized", + "tags": [], + "label": "cpuTotal", + "description": [], + "signature": [ + "null" + ], + "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "metricsDataAccess", "id": "def-common.SnapshotMetricTypeKeys.cpu", @@ -2496,7 +2510,7 @@ "description": [], "signature": [ "KeyofC", - "<{ count: null; cpu: null; diskLatency: null; diskSpaceUsage: null; load: null; memory: null; memoryFree: null; memoryTotal: null; normalizedLoad1m: null; tx: null; rx: null; txV2: null; rxV2: null; logRate: null; diskIOReadBytes: null; diskIOWriteBytes: null; s3TotalRequests: null; s3NumberOfObjects: null; s3BucketSize: null; s3DownloadBytes: null; s3UploadBytes: null; rdsConnections: null; rdsQueriesExecuted: null; rdsActiveTransactions: null; rdsLatency: null; sqsMessagesVisible: null; sqsMessagesDelayed: null; sqsMessagesSent: null; sqsMessagesEmpty: null; sqsOldestMessage: null; custom: null; }>" + "<{ count: null; cpuTotal: null; cpu: null; diskLatency: null; diskSpaceUsage: null; load: null; memory: null; memoryFree: null; memoryTotal: null; normalizedLoad1m: null; tx: null; rx: null; txV2: null; rxV2: null; logRate: null; diskIOReadBytes: null; diskIOWriteBytes: null; s3TotalRequests: null; s3NumberOfObjects: null; s3BucketSize: null; s3DownloadBytes: null; s3UploadBytes: null; rdsConnections: null; rdsQueriesExecuted: null; rdsActiveTransactions: null; rdsLatency: null; sqsMessagesVisible: null; sqsMessagesDelayed: null; sqsMessagesSent: null; sqsMessagesEmpty: null; sqsOldestMessage: null; custom: null; }>" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts", "deprecated": false, diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index 5084b4f7826a2..6e3d77f031903 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.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 | |-------------------|-----------|------------------------|-----------------| -| 107 | 8 | 107 | 6 | +| 108 | 8 | 108 | 6 | ## Client diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index fa9f6f6691d61..a5fd03d22f8be 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2024-08-06 +date: 2024-08-09 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 a19af8c8f4576..5e7b080d1c64c 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-08-06 +date: 2024-08-09 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 9d729888e6da9..8b241b5c6f388 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2024-08-06 +date: 2024-08-09 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 b78684744377d..f8af3c0b1736e 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 78fbde3b94fcf..0ac7656423f6c 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index ebdfc6470eeba..8ad140c1d9143 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 7ec63565e2c18..34e4ced8c545f 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 26c0499f87436..cd598758d56be 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2024-08-06 +date: 2024-08-09 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 e26ad3247d566..472391e133e2d 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -1042,7 +1042,7 @@ "label": "useAnnotations", "description": [], "signature": [ - "({ domain, editAnnotation, slo, setEditAnnotation, }?: { slo?: ({ 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: string; } | undefined; }) | undefined; editAnnotation?: ({ id: string; } & { annotation: { title?: string | undefined; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; } & { event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; }) | null | undefined; setEditAnnotation?: ((annotation: ({ id: string; } & { annotation: { title?: string | undefined; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; } & { event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; }) | null) => void) | undefined; domain?: { min: string | number; max: string | number; } | undefined; }) => { annotations: ({ id: string; } & { annotation: { title?: string | undefined; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; } & { event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; })[]; onAnnotationClick: (annotations: { rects: ", + "({ domain, editAnnotation, slo, setEditAnnotation, }?: { slo?: ({ 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: string; } | undefined; }) | undefined; editAnnotation?: ({ id: string; } & { annotation: { title?: string | undefined; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; } & { event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; }) | null | undefined; setEditAnnotation?: ((annotation: ({ id: string; } & { annotation: { title?: string | undefined; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; } & { event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; }) | null) => void) | undefined; domain?: { min: string | number; max: string | number; } | undefined; }) => { annotations: ({ id: string; } & { annotation: { title?: string | undefined; type?: string | undefined; style?: { icon?: string | undefined; color?: string | undefined; line?: { width?: number | undefined; style?: \"dashed\" | \"solid\" | \"dotted\" | undefined; iconPosition?: \"top\" | \"bottom\" | undefined; textDecoration?: \"none\" | \"name\" | undefined; } | undefined; rect?: { fill?: \"inside\" | \"outside\" | undefined; } | undefined; } | undefined; }; '@timestamp': string; message: string; } & { event?: ({ start: string; } & { end?: string | undefined; }) | undefined; tags?: string[] | undefined; service?: { name?: string | undefined; environment?: string | undefined; version?: string | undefined; } | undefined; monitor?: { id?: string | undefined; } | undefined; slo?: ({ id: string; } & { instanceId?: string | undefined; }) | undefined; host?: { name?: string | undefined; } | undefined; })[]; onAnnotationClick: (annotations: { rects: ", "RectAnnotationEvent", "[]; lines: ", "LineAnnotationEvent", @@ -1079,7 +1079,7 @@ "label": "slo", "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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: string; } | undefined; }) | 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; settings: { syncDelay: string; frequency: string; preventInitialBackfill: boolean; }; revision: number; enabled: boolean; tags: string[]; createdAt: string; updatedAt: string; groupBy: string | string[]; version: number; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; fiveMinuteBurnRate: number; oneHourBurnRate: number; oneDayBurnRate: number; } & { summaryUpdatedAt?: string | null | undefined; }; groupings: { [x: string]: string | number; }; } & { instanceId?: string | undefined; meta?: { synthetics?: { monitorId: string; locationId: string; configId: string; } | undefined; } | undefined; remote?: { remoteName: string; kibanaUrl: string; } | undefined; }) | undefined" ], "path": "x-pack/plugins/observability_solution/observability/public/components/annotations/use_annotations.tsx", "deprecated": false, diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index ba7af7d14a13b..dfeb8dd25f1c9 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index a988494433897..542a4ff27ca5d 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 640ca66b24111..e2e99cac8af92 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 35fd01d1426ba..845a6410839a9 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index c4aeec3550381..a80d8fe2af855 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 1da558c208681..450b385bb3cdb 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index ba0f57aa28532..096ec480d34a1 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-08-06 +date: 2024-08-09 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 8f0fe573bef52..191871f79f468 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-08-06 +date: 2024-08-09 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 b42d6950f6d82..8b5cd8287264b 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-08-06 +date: 2024-08-09 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 8e763328af648..a01de278ae337 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-08-06 +date: 2024-08-09 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 | |--------------|----------|------------------------| -| 827 | 703 | 45 | +| 828 | 704 | 45 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 51897 | 241 | 38870 | 1894 | +| 51995 | 241 | 38936 | 1911 | ## Plugin Directory @@ -33,8 +33,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 74 | 0 | 9 | 2 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 871 | 1 | 839 | 52 | | | [@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 | 119 | -| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 74 | 0 | 74 | 0 | -| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 2 | 0 | 2 | 0 | +| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 74 | 0 | 74 | 1 | | | [@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. | 83 | 1 | 73 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | @@ -48,7 +47,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | cloudFullStory | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | When Kibana runs on Elastic Cloud, this plugin registers FullStory as a shipper for telemetry. | 0 | 0 | 0 | 0 | | cloudLinks | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Adds the links to the Elastic Cloud console | 0 | 0 | 0 | 0 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | The cloud security posture plugin | 14 | 0 | 2 | 2 | -| | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 38 | 0 | 30 | 0 | +| | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 39 | 0 | 30 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 149 | 0 | 125 | 6 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 351 | 0 | 343 | 19 | | crossClusterReplication | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 0 | 0 | 0 | 0 | @@ -56,12 +55,12 @@ 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 | 271 | 0 | 252 | 1 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 129 | 0 | 123 | 12 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3205 | 31 | 2590 | 24 | +| | [@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. | 3209 | 31 | 2594 | 24 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 5 | 0 | 5 | 0 | | | [@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 | 1 | | | [@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. | 1170 | 0 | 401 | 3 | +| | [@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. | 1224 | 0 | 443 | 3 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 31 | 3 | 25 | 4 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin introduces the concept of data set quality, where users can easily get an overview on the data sets they have. | 10 | 0 | 10 | 5 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 15 | 0 | 9 | 2 | @@ -74,6 +73,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Extends embeddable plugin with more functionality | 19 | 0 | 19 | 2 | | | [@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/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | Adds dashboards for discovering and managing Enterprise Search products. | 5 | 0 | 5 | 0 | +| | [@elastic/obs-entities](https://github.com/orgs/elastic/teams/obs-entities) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-entities](https://github.com/orgs/elastic/teams/obs-entities) | Entity manager plugin for entity assets (inventory, topology, etc) | 16 | 0 | 16 | 1 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 99 | 3 | 97 | 3 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 24 | 0 | 9 | 0 | @@ -102,7 +102,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 84 | 0 | 84 | 8 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 24 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 3 | 0 | 3 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1351 | 5 | 1229 | 73 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1351 | 5 | 1229 | 74 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 72 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -114,14 +114,15 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Image embeddable | 1 | 0 | 1 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 227 | 0 | 222 | 1 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 16 | 0 | 14 | 11 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin visualizes data from Filebeat and Metricbeat, and integrates with other Observability solutions | 37 | 0 | 34 | 6 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 4 | 0 | 4 | 0 | | inputControlVis | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Input Control visualization to Kibana | 0 | 0 | 0 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 127 | 2 | 100 | 4 | | | [@elastic/security-scalability](https://github.com/orgs/elastic/teams/security-scalability) | Plugin implementing the Integration Assistant API and UI | 47 | 0 | 40 | 3 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides UI and APIs for the interactive setup mode. | 28 | 0 | 18 | 0 | -| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 133 | 0 | 133 | 6 | -| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 5 | 0 | 5 | 0 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 105 | 0 | 105 | 6 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 5 | 0 | 5 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 6 | 0 | 6 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 153 | 0 | 121 | 3 | | kibanaUsageCollection | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -133,14 +134,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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. | 5 | 0 | 5 | 0 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 226 | 0 | 97 | 52 | -| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 7 | 0 | 7 | 6 | +| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 9 | 0 | 9 | 6 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin provides a LogsExplorer component using the Discover customization framework, offering several affordances specifically designed for log consumption. | 117 | 4 | 117 | 22 | -| | [@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. | 297 | 0 | 269 | 32 | +| | [@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. | 300 | 0 | 272 | 32 | | logstash | [@elastic/logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 44 | 0 | 44 | 7 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 209 | 0 | 205 | 28 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 60 | 0 | 60 | 0 | -| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | Exposes utilities for accessing metrics data | 107 | 8 | 107 | 6 | +| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | Exposes utilities for accessing metrics data | 108 | 8 | 108 | 6 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 154 | 3 | 67 | 102 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 2 | 0 | 2 | 0 | | | [@elastic/stack-monitoring](https://github.com/orgs/elastic/teams/stack-monitoring) | - | 15 | 3 | 13 | 1 | @@ -182,7 +183,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/search-kibana](https://github.com/orgs/elastic/teams/search-kibana) | - | 15 | 0 | 9 | 1 | | searchprofiler | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 0 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 447 | 0 | 231 | 1 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 190 | 0 | 121 | 36 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 192 | 0 | 123 | 33 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | ESS customizations for Security Solution. | 6 | 0 | 6 | 0 | | | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Serverless customizations for security. | 7 | 0 | 7 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | The core Serverless plugin, providing APIs to Serverless Project plugins. | 25 | 0 | 24 | 0 | @@ -196,13 +197,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 25 | 0 | 25 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 10 | 0 | 10 | 0 | | synthetics | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 1 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 105 | 0 | 62 | 5 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 107 | 0 | 63 | 7 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 45 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 31 | 0 | 26 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 6 | 0 | 0 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | Elastic threat intelligence helps you see if you are open to or have been subject to current or historical known threats | 30 | 0 | 14 | 4 | -| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 226 | 1 | 182 | 18 | +| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 226 | 1 | 182 | 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) | - | 590 | 1 | 564 | 51 | @@ -493,13 +494,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 156 | 0 | 130 | 9 | | | [@elastic/security-generative-ai](https://github.com/orgs/elastic/teams/security-generative-ai) | - | 354 | 0 | 328 | 0 | | | [@elastic/obs-entities](https://github.com/orgs/elastic/teams/obs-entities) | - | 26 | 0 | 26 | 0 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 54 | 0 | 39 | 7 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 55 | 0 | 40 | 7 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 32 | 0 | 19 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 11 | 0 | 6 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 269 | 1 | 209 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 27 | 0 | 27 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 135 | 1 | 120 | 13 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 149 | 1 | 120 | 15 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 66 | 0 | 62 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 194 | 0 | 183 | 10 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 40 | 0 | 40 | 0 | @@ -509,7 +510,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 49 | 0 | 40 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 0 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 3 | 0 | 3 | 0 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 39 | 0 | 24 | 1 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 46 | 0 | 31 | 1 | | | [@elastic/appex-qa](https://github.com/orgs/elastic/teams/appex-qa) | - | 551 | 6 | 511 | 3 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 0 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 1 | 0 | 1 | 0 | @@ -549,7 +550,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 23 | 0 | 7 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 8 | 0 | 2 | 3 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 45 | 0 | 0 | 0 | -| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 139 | 0 | 137 | 0 | +| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 143 | 0 | 141 | 0 | | | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 20 | 0 | 11 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 88 | 0 | 10 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 56 | 0 | 6 | 0 | @@ -579,7 +580,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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) | - | 34 | 0 | 0 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 43 | 0 | 38 | 1 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 44 | 0 | 38 | 1 | | | [@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/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 22 | 0 | 16 | 0 | @@ -669,7 +670,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 209 | 0 | 161 | 0 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 28 | 0 | 25 | 0 | | | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 120 | 0 | 116 | 0 | -| | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 49 | 0 | 44 | 0 | +| | [@elastic/security-detection-engine](https://github.com/orgs/elastic/teams/security-detection-engine) | - | 51 | 0 | 46 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 69 | 0 | 64 | 0 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 31 | 0 | 30 | 1 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 5 | 0 | 5 | 0 | @@ -727,7 +728,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 41 | 2 | 21 | 0 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 32 | 2 | 32 | 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) | - | 315 | 4 | 267 | 13 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 315 | 4 | 267 | 14 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 36 | 0 | 18 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 131 | 3 | 98 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | @@ -741,7 +742,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 42 | 0 | 28 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 57 | 0 | 48 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 9 | 0 | 8 | 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 | 154 | 0 | 81 | 1 | +| | [@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 | 166 | 0 | 92 | 2 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 18 | 0 | 17 | 5 | | | [@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 | 314 | 0 | 285 | 8 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 13 | 0 | 9 | 0 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index dcb285c7c8988..201888af2ba9c 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index a3c1eb567fec6..4b03c2ea0f413 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-08-06 +date: 2024-08-09 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 e1a06ba7b4afb..fd6064d37fbbe 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index a23543af3f0ca..5f7bced63aa1e 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-08-06 +date: 2024-08-09 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 c4767e740b4c4..212774f690ccd 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-08-06 +date: 2024-08-09 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 b130172601fed..07500f7076982 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-08-06 +date: 2024-08-09 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 4870b174d260d..83f9dfb5d0207 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 1424baedb07e3..32539cc037213 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-08-06 +date: 2024-08-09 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 3bb91dabb2a08..fe3b0b92b01cc 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-08-06 +date: 2024-08-09 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 0bad5050c8acb..87e916666621d 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-08-06 +date: 2024-08-09 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 a60dc6423cebc..ded22b051fbab 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-08-06 +date: 2024-08-09 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 169b5d342d061..3409f9faff25f 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-08-06 +date: 2024-08-09 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 7505b5952f70e..24253bbbc421b 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-08-06 +date: 2024-08-09 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 2447c683b513b..e19caa8827522 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-08-06 +date: 2024-08-09 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 8654d852d1a7a..6f51293e8df31 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index d78fc1b3ee93a..9a1653fb4e6d5 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 665970740e4a3..cbf7ad95d1d5a 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 55d0507ef66db..2771db829e225 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_homepage.mdx b/api_docs/search_homepage.mdx index 17c2d8f35fa64..9c30bc2e258f0 100644 --- a/api_docs/search_homepage.mdx +++ b/api_docs/search_homepage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchHomepage title: "searchHomepage" image: https://source.unsplash.com/400x175/?github description: API docs for the searchHomepage plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchHomepage'] --- import searchHomepageObj from './search_homepage.devdocs.json'; diff --git a/api_docs/search_inference_endpoints.mdx b/api_docs/search_inference_endpoints.mdx index 23434536b874e..4546d7de15e23 100644 --- a/api_docs/search_inference_endpoints.mdx +++ b/api_docs/search_inference_endpoints.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchInferenceEndpoints title: "searchInferenceEndpoints" image: https://source.unsplash.com/400x175/?github description: API docs for the searchInferenceEndpoints plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchInferenceEndpoints'] --- import searchInferenceEndpointsObj from './search_inference_endpoints.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index bc3f21be66f6b..8cffd670a4e06 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index e4178a0067532..fea42a0515304 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 223e847d5a4d4..f99cd963ebcbb 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-08-06 +date: 2024-08-09 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 8a886132d9291..fef443a955c1e 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -390,7 +390,7 @@ "label": "data", "description": [], "signature": [ - "({ id: string; type: \"eql\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; language: \"eql\"; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; tiebreaker_field?: string | undefined; timestamp_field?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; event_category_override?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { id: string; type: \"query\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; saved_id?: string | undefined; response_actions?: ({ params: { query?: string | undefined; timeout?: number | undefined; queries?: { id: string; query: string; version?: string | undefined; snapshot?: boolean | undefined; platform?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; removed?: boolean | undefined; }[] | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; saved_query_id?: string | undefined; pack_id?: string | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; } | { config: { field: string; overwrite: boolean; }; command: \"kill-process\" | \"suspend-process\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; } | { id: string; type: \"saved_query\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; saved_id: string; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; query?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; response_actions?: ({ params: { query?: string | undefined; timeout?: number | undefined; queries?: { id: string; query: string; version?: string | undefined; snapshot?: boolean | undefined; platform?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; removed?: boolean | undefined; }[] | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; saved_query_id?: string | undefined; pack_id?: string | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; } | { config: { field: string; overwrite: boolean; }; command: \"kill-process\" | \"suspend-process\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; } | { id: string; type: \"threshold\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threshold: { value: number; field: string | string[]; cardinality?: { value: number; field: string; }[] | undefined; }; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { duration: { value: number; unit: \"m\" | \"h\" | \"s\"; }; } | undefined; saved_id?: string | undefined; } | { id: string; type: \"threat_match\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; threat_query: string; threat_mapping: { entries: { value: string; type: \"mapping\"; field: string; }[]; }[]; threat_index: string[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; saved_id?: string | undefined; threat_filters?: unknown[] | undefined; threat_indicator_path?: string | undefined; threat_language?: \"kuery\" | \"lucene\" | undefined; concurrent_searches?: number | undefined; items_per_search?: number | undefined; } | { id: string; type: \"machine_learning\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; anomaly_threshold: number; machine_learning_job_id: string | string[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { id: string; type: \"new_terms\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; language: \"kuery\" | \"lucene\"; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; new_terms_fields: string[]; history_window_start: string; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { id: string; type: \"esql\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; language: \"esql\"; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; })[]" + "({ id: string; type: \"eql\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; language: \"eql\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; tiebreaker_field?: string | undefined; timestamp_field?: string | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; event_category_override?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { id: string; type: \"query\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; language: \"kuery\" | \"lucene\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; saved_id?: string | undefined; response_actions?: ({ params: { query?: string | undefined; timeout?: number | undefined; queries?: { id: string; query: string; version?: string | undefined; snapshot?: boolean | undefined; platform?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; removed?: boolean | undefined; }[] | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; saved_query_id?: string | undefined; pack_id?: string | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; } | { config: { field: string; overwrite: boolean; }; command: \"kill-process\" | \"suspend-process\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; } | { id: string; type: \"saved_query\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; language: \"kuery\" | \"lucene\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; saved_id: string; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; query?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; response_actions?: ({ params: { query?: string | undefined; timeout?: number | undefined; queries?: { id: string; query: string; version?: string | undefined; snapshot?: boolean | undefined; platform?: string | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; removed?: boolean | undefined; }[] | undefined; ecs_mapping?: Zod.objectOutputType<{}, Zod.ZodObject<{ field: Zod.ZodOptional; value: Zod.ZodOptional]>>; }, \"strip\", Zod.ZodTypeAny, { value?: string | string[] | undefined; field?: string | undefined; }, { value?: string | string[] | undefined; field?: string | undefined; }>, \"strip\"> | undefined; saved_query_id?: string | undefined; pack_id?: string | undefined; }; action_type_id: \".osquery\"; } | { params: { command: \"isolate\"; comment?: string | undefined; } | { config: { field: string; overwrite: boolean; }; command: \"kill-process\" | \"suspend-process\"; comment?: string | undefined; }; action_type_id: \".endpoint\"; })[] | undefined; } | { id: string; type: \"threshold\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; language: \"kuery\" | \"lucene\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threshold: { value: number; field: string | string[]; cardinality?: { value: number; field: string; }[] | undefined; }; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { duration: { value: number; unit: \"m\" | \"h\" | \"s\"; }; } | undefined; saved_id?: string | undefined; } | { id: string; type: \"threat_match\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; language: \"kuery\" | \"lucene\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; threat_query: string; threat_mapping: { entries: { value: string; type: \"mapping\"; field: string; }[]; }[]; threat_index: string[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; saved_id?: string | undefined; threat_filters?: unknown[] | undefined; threat_indicator_path?: string | undefined; threat_language?: \"kuery\" | \"lucene\" | undefined; concurrent_searches?: number | undefined; items_per_search?: number | undefined; } | { id: string; type: \"machine_learning\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; anomaly_threshold: number; machine_learning_job_id: string | string[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { id: string; type: \"new_terms\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; language: \"kuery\" | \"lucene\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; new_terms_fields: string[]; history_window_start: string; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; index?: string[] | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; filters?: unknown[] | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; data_view_id?: string | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; } | { id: string; type: \"esql\"; version: number; name: string; actions: { params: {} & { [k: string]: unknown; }; id: string; action_type_id: string; frequency?: { throttle: string | null; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; summary: boolean; } | undefined; uuid?: string | undefined; group?: string | undefined; alerts_filter?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; }[]; tags: string[]; setup: string; description: string; enabled: boolean; revision: number; query: string; interval: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; risk_score: number; language: \"esql\"; from: string; to: string; created_at: string; created_by: string; updated_at: string; updated_by: string; references: string[]; author: string[]; immutable: boolean; rule_id: string; threat: { framework: string; tactic: { id: string; name: string; reference: string; }; technique?: { id: string; name: string; reference: string; subtechnique?: { id: string; name: string; reference: string; }[] | undefined; }[] | undefined; }[]; risk_score_mapping: { value: string; field: string; operator: \"equals\"; risk_score?: number | undefined; }[]; severity_mapping: { value: string; severity: \"medium\" | \"high\" | \"low\" | \"critical\"; field: string; operator: \"equals\"; }[]; exceptions_list: { id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; list_id: string; namespace_type: \"single\" | \"agnostic\"; }[]; false_positives: string[]; max_signals: number; related_integrations: { version: string; package: string; integration?: string | undefined; }[]; required_fields: { type: string; name: string; ecs: boolean; }[]; meta?: Zod.objectOutputType<{}, Zod.ZodUnknown, \"strip\"> | undefined; namespace?: string | undefined; license?: string | undefined; throttle?: string | undefined; outcome?: \"exactMatch\" | \"aliasMatch\" | \"conflict\" | undefined; alias_target_id?: string | undefined; alias_purpose?: \"savedObjectConversion\" | \"savedObjectImport\" | undefined; note?: string | undefined; rule_name_override?: string | undefined; timestamp_override?: string | undefined; timestamp_override_fallback_disabled?: boolean | undefined; timeline_id?: string | undefined; timeline_title?: string | undefined; building_block_type?: string | undefined; output_index?: string | undefined; investigation_fields?: { field_names: string[]; } | undefined; rule_source?: { type: \"external\"; is_customized: boolean; } | { type: \"internal\"; } | undefined; execution_summary?: { last_execution: { message: string; date: string; status: \"running\" | \"succeeded\" | \"failed\" | \"going to run\" | \"partial failure\"; metrics: { total_search_duration_ms?: number | undefined; total_indexing_duration_ms?: number | undefined; execution_gap_duration_s?: number | undefined; total_enrichment_duration_ms?: number | undefined; }; status_order: number; }; } | undefined; alert_suppression?: { group_by: string[]; duration?: { value: number; unit: \"m\" | \"h\" | \"s\"; } | undefined; missing_fields_strategy?: \"doNotSuppress\" | \"suppress\" | undefined; } | undefined; })[]" ], "path": "x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/types.ts", "deprecated": false, @@ -1073,14 +1073,14 @@ { "parentPluginId": "securitySolution", "id": "def-public.TimelineModel.timelineType", - "type": "Enum", + "type": "CompoundType", "tags": [], "label": "timelineType", "description": [ "timelineType: default | template" ], "signature": [ - "TimelineType" + "\"default\" | \"template\"" ], "path": "x-pack/plugins/security_solution/public/timelines/store/model.ts", "deprecated": false, @@ -1240,14 +1240,14 @@ { "parentPluginId": "securitySolution", "id": "def-public.TimelineModel.status", - "type": "Enum", + "type": "CompoundType", "tags": [], "label": "status", "description": [ "status: active | draft" ], "signature": [ - "TimelineStatus" + "\"active\" | \"draft\" | \"immutable\"" ], "path": "x-pack/plugins/security_solution/public/timelines/store/model.ts", "deprecated": false, @@ -1463,8 +1463,7 @@ "label": "excludedRowRendererIds", "description": [], "signature": [ - "RowRendererId", - "[]" + "(\"alert\" | \"alerts\" | \"plain\" | \"system\" | \"registry\" | \"auditd\" | \"auditd_file\" | \"library\" | \"netflow\" | \"suricata\" | \"system_dns\" | \"system_endgame_process\" | \"system_file\" | \"system_fim\" | \"system_security_event\" | \"system_socket\" | \"threat_match\" | \"zeek\")[]" ], "path": "x-pack/plugins/security_solution/public/timelines/store/model.ts", "deprecated": false, @@ -2202,6 +2201,27 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "securitySolution", + "id": "def-server.AppClient.Unnamed.$5", + "type": "CompoundType", + "tags": [], + "label": "buildFlavor", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.BuildFlavor", + "text": "BuildFlavor" + } + ], + "path": "x-pack/plugins/security_solution/server/client/client.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [] @@ -2317,6 +2337,29 @@ "trackAdoption": false, "children": [], "returnComment": [] + }, + { + "parentPluginId": "securitySolution", + "id": "def-server.AppClient.getBuildFlavor", + "type": "Function", + "tags": [], + "label": "getBuildFlavor", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/config", + "scope": "server", + "docId": "kibKbnConfigPluginApi", + "section": "def-server.BuildFlavor", + "text": "BuildFlavor" + } + ], + "path": "x-pack/plugins/security_solution/server/client/client.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index d1012778057c8..4e9f907ac2dec 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 190 | 0 | 121 | 36 | +| 192 | 0 | 123 | 33 | ## Client diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index a3b90f5f5ea9d..e49f4f80e0e96 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-08-06 +date: 2024-08-09 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 1d46e324f9a31..067e227e2739f 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 3b3b8c40033f0..db39d71671e48 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index 2e9ffb1c9f681..8dc2406690cc9 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-08-06 +date: 2024-08-09 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 c32539606b0ed..7e0d68bb81986 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-08-06 +date: 2024-08-09 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 b8830b7e9572d..94ce04d88d052 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 7ddd4b85ed80c..c1fe088ff1776 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.devdocs.json b/api_docs/slo.devdocs.json index 8b0bb5f9da3e2..b8669843b1816 100644 --- a/api_docs/slo.devdocs.json +++ b/api_docs/slo.devdocs.json @@ -1082,7 +1082,7 @@ }, "<", "CreateSLOForm", - "<{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }>> | undefined; }) => JSX.Element; }" + "<{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: 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 | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: string; filters: { meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; field?: string | undefined; params?: any; value?: string | undefined; }; query: { [x: string]: any; }; }[]; } | undefined; dataViewId?: string | undefined; }; }>> | undefined; }) => JSX.Element; }" ], "path": "x-pack/plugins/observability_solution/slo/public/types.ts", "deprecated": false, diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 8594202bb5125..bd15f6ad56b19 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 00b301e345b7d..ae4b974ca9611 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-08-06 +date: 2024-08-09 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 1482d7ccc37d4..5b974623c8d2c 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-08-06 +date: 2024-08-09 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 8b00528ccfb37..234a53cdde1b8 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.devdocs.json b/api_docs/stack_connectors.devdocs.json index 546dd6308eb8d..5c0fdb71ca39b 100644 --- a/api_docs/stack_connectors.devdocs.json +++ b/api_docs/stack_connectors.devdocs.json @@ -30,7 +30,7 @@ "section": "def-common.Type", "text": "Type" }, - "; }> | Readonly<{} & { subAction: \"getIncident\"; subActionParams: Readonly<{} & { externalId: string; }>; }> | Readonly<{} & { subAction: \"handshake\"; subActionParams: Readonly<{} & {}>; }> | Readonly<{} & { subAction: \"pushToService\"; subActionParams: Readonly<{} & { comments: Readonly<{} & { comment: string; commentId: string; }>[] | null; incident: Readonly<{} & { description: string | null; summary: string; labels: string[] | null; issueType: string | null; priority: string | null; parent: string | null; externalId: string | null; otherFields: Record | null; }>; }>; }> | Readonly<{} & { subAction: \"issueTypes\"; subActionParams: Readonly<{} & {}>; }> | Readonly<{} & { subAction: \"fieldsByIssueType\"; subActionParams: Readonly<{} & { id: string; }>; }> | Readonly<{} & { subAction: \"issues\"; subActionParams: Readonly<{} & { title: string; }>; }> | Readonly<{} & { subAction: \"issue\"; subActionParams: Readonly<{} & { id: string; }>; }>>" + "; }> | Readonly<{} & { subAction: \"getIncident\"; subActionParams: Readonly<{} & { externalId: string; }>; }> | Readonly<{} & { subAction: \"handshake\"; subActionParams: Readonly<{} & {}>; }> | Readonly<{} & { subAction: \"pushToService\"; subActionParams: Readonly<{} & { comments: Readonly<{} & { comment: string; commentId: string; }>[] | null; incident: Readonly<{} & { description: string | null; summary: string; labels: string[] | null; parent: string | null; issueType: string | null; priority: string | null; externalId: string | null; otherFields: Record | null; }>; }>; }> | Readonly<{} & { subAction: \"issueTypes\"; subActionParams: Readonly<{} & {}>; }> | Readonly<{} & { subAction: \"fieldsByIssueType\"; subActionParams: Readonly<{} & { id: string; }>; }> | Readonly<{} & { subAction: \"issues\"; subActionParams: Readonly<{} & { title: string; }>; }> | Readonly<{} & { subAction: \"issue\"; subActionParams: Readonly<{} & { id: string; }>; }>>" ], "path": "x-pack/plugins/stack_connectors/server/connector_types/jira/schema.ts", "deprecated": false, diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 316e0f90fb3b8..d053eac7afb78 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.devdocs.json b/api_docs/task_manager.devdocs.json index 1b43d99d8b0be..6ed73468daeea 100644 --- a/api_docs/task_manager.devdocs.json +++ b/api_docs/task_manager.devdocs.json @@ -230,7 +230,7 @@ "label": "start", "description": [], "signature": [ - "({ savedObjects, elasticsearch, executionContext, docLinks, }: ", + "({ savedObjects, elasticsearch, executionContext, docLinks }: ", { "pluginId": "@kbn/core-lifecycle-server", "scope": "server", @@ -238,6 +238,8 @@ "section": "def-server.CoreStart", "text": "CoreStart" }, + ", { cloud }: ", + "TaskManagerPluginStart", ") => ", { "pluginId": "taskManager", @@ -256,7 +258,7 @@ "id": "def-server.TaskManagerPlugin.start.$1", "type": "Object", "tags": [], - "label": "{\n savedObjects,\n elasticsearch,\n executionContext,\n docLinks,\n }", + "label": "{ savedObjects, elasticsearch, executionContext, docLinks }", "description": [], "signature": [ { @@ -271,6 +273,21 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskManagerPlugin.start.$2", + "type": "Object", + "tags": [], + "label": "{ cloud }", + "description": [], + "signature": [ + "TaskManagerPluginStart" + ], + "path": "x-pack/plugins/task_manager/server/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true } ], "returnComment": [] @@ -1437,6 +1454,23 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "taskManager", + "id": "def-server.TaskRegisterDefinition.cost", + "type": "CompoundType", + "tags": [], + "label": "cost", + "description": [ + "\nAn optional definition of the cost associated with running the task." + ], + "signature": [ + "TaskCost", + " | undefined" + ], + "path": "x-pack/plugins/task_manager/server/task_type_dictionary.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "taskManager", "id": "def-server.TaskRegisterDefinition.description", diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 36bbb52fe5187..9adba47978355 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.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 | |-------------------|-----------|------------------------|-----------------| -| 105 | 0 | 62 | 5 | +| 107 | 0 | 63 | 7 | ## Server diff --git a/api_docs/telemetry.devdocs.json b/api_docs/telemetry.devdocs.json index 20738f47f4682..ae2e19bf1a858 100644 --- a/api_docs/telemetry.devdocs.json +++ b/api_docs/telemetry.devdocs.json @@ -630,7 +630,7 @@ "When the data comes from a matching index-pattern, the name of the pattern" ], "signature": [ - "\"search\" | \"logstash\" | \"alerts\" | \"apm\" | \"metricbeat\" | \"enterprise-search\" | \"app-search\" | \"magento2\" | \"magento\" | \"shopify\" | \"wordpress\" | \"drupal\" | \"joomla\" | \"sharepoint\" | \"squarespace\" | \"sitecore\" | \"weebly\" | \"acquia\" | \"filebeat\" | \"generic-filebeat\" | \"generic-metricbeat\" | \"functionbeat\" | \"generic-functionbeat\" | \"heartbeat\" | \"generic-heartbeat\" | \"generic-logstash\" | \"fluentd\" | \"telegraf\" | \"prometheusbeat\" | \"fluentbit\" | \"nginx\" | \"apache\" | \"generic-logs\" | \"endgame\" | \"logs-endpoint\" | \"metrics-endpoint\" | \"siem-signals\" | \"auditbeat\" | \"winlogbeat\" | \"packetbeat\" | \"tomcat\" | \"artifactory\" | \"aruba\" | \"barracuda\" | \"bluecoat\" | \"arcsight\" | \"checkpoint\" | \"cisco\" | \"citrix\" | \"cyberark\" | \"cylance\" | \"fireeye\" | \"fortinet\" | \"infoblox\" | \"kaspersky\" | \"mcafee\" | \"paloaltonetworks\" | \"rsa\" | \"snort\" | \"sonicwall\" | \"sophos\" | \"squid\" | \"symantec\" | \"tippingpoint\" | \"trendmicro\" | \"tripwire\" | \"zscaler\" | \"zeek\" | \"sigma_doc\" | \"ecs-corelight\" | \"suricata\" | \"wazuh\" | \"meow\" | \"host_risk_score\" | \"user_risk_score\" | undefined" + "\"search\" | \"logstash\" | \"alerts\" | \"apm\" | \"metricbeat\" | \"suricata\" | \"zeek\" | \"enterprise-search\" | \"app-search\" | \"magento2\" | \"magento\" | \"shopify\" | \"wordpress\" | \"drupal\" | \"joomla\" | \"sharepoint\" | \"squarespace\" | \"sitecore\" | \"weebly\" | \"acquia\" | \"filebeat\" | \"generic-filebeat\" | \"generic-metricbeat\" | \"functionbeat\" | \"generic-functionbeat\" | \"heartbeat\" | \"generic-heartbeat\" | \"generic-logstash\" | \"fluentd\" | \"telegraf\" | \"prometheusbeat\" | \"fluentbit\" | \"nginx\" | \"apache\" | \"generic-logs\" | \"endgame\" | \"logs-endpoint\" | \"metrics-endpoint\" | \"siem-signals\" | \"auditbeat\" | \"winlogbeat\" | \"packetbeat\" | \"tomcat\" | \"artifactory\" | \"aruba\" | \"barracuda\" | \"bluecoat\" | \"arcsight\" | \"checkpoint\" | \"cisco\" | \"citrix\" | \"cyberark\" | \"cylance\" | \"fireeye\" | \"fortinet\" | \"infoblox\" | \"kaspersky\" | \"mcafee\" | \"paloaltonetworks\" | \"rsa\" | \"snort\" | \"sonicwall\" | \"sophos\" | \"squid\" | \"symantec\" | \"tippingpoint\" | \"trendmicro\" | \"tripwire\" | \"zscaler\" | \"sigma_doc\" | \"ecs-corelight\" | \"wazuh\" | \"meow\" | \"host_risk_score\" | \"user_risk_score\" | undefined" ], "path": "src/plugins/telemetry/server/telemetry_collection/get_data_telemetry/get_data_telemetry.ts", "deprecated": false, diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index cd6307951aa12..32a23ceb691eb 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-08-06 +date: 2024-08-09 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 b20e5b863ebda..ea5e016796087 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-08-06 +date: 2024-08-09 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 cbef64a5891b9..f6a0d757e96e1 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2024-08-06 +date: 2024-08-09 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 66a6f09e89d3a..4a0163d07d058 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 7a3d32d044ce3..c8c7531cc6444 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-08-06 +date: 2024-08-09 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 159e7ebdd1ce6..0d8299cd0d6d7 100644 --- a/api_docs/timelines.devdocs.json +++ b/api_docs/timelines.devdocs.json @@ -1642,8 +1642,7 @@ "\nReturns a DataProviderType" ], "signature": [ - "DataProviderType", - " | undefined" + "\"default\" | \"template\" | undefined" ], "path": "x-pack/plugins/timelines/common/types/timeline/data_provider/index.ts", "deprecated": false, @@ -4007,9 +4006,7 @@ "label": "DataProvidersAnd", "description": [], "signature": [ - "{ id: string; type?: ", - "DataProviderType", - " | undefined; name: string; enabled: boolean; excluded: boolean; kqlQuery: string; queryMatch: ", + "{ id: string; type?: \"default\" | \"template\" | undefined; name: string; enabled: boolean; excluded: boolean; kqlQuery: string; queryMatch: ", { "pluginId": "timelines", "scope": "common", diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index b11223112e379..62283ec54596b 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-threat-hunting-investigations](https://github.com/org | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 226 | 1 | 182 | 18 | +| 226 | 1 | 182 | 17 | ## Client diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index fad1761726f53..c3e4ab112251d 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index e8a95dcecf5c7..6ad099fb50dcd 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 96a0cc64251ba..614152b1c03c5 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 1d9e63c1737ac..077155e7aa49d 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index 747e2d7610456..69bd2664ffcf1 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 6e55d53020edf..c35c0e0e23c5e 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-08-06 +date: 2024-08-09 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 796b0d0534d45..6762fdce473d3 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-08-06 +date: 2024-08-09 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 17f0bdd365afc..9d34c07a0e8bc 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-08-06 +date: 2024-08-09 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 4927c4a7a64c4..4c4db200e1ddd 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-08-06 +date: 2024-08-09 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 e3424a5bd8143..37aad36ae4d0d 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-08-06 +date: 2024-08-09 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 a657eafa32895..aec2f872d8bba 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-08-06 +date: 2024-08-09 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 ae8e6e21a12bd..9fd3659c61e8b 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-08-06 +date: 2024-08-09 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 bd0b7738ffe80..f4554b6b833f0 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-08-06 +date: 2024-08-09 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 5ab3ee3b0c4ee..b9ebb861681a6 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-08-06 +date: 2024-08-09 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 2ab303a98dcf9..011a5448fcb72 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-08-06 +date: 2024-08-09 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 68777a31c7377..4977547589cdd 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-08-06 +date: 2024-08-09 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 b29dbf3db9080..70158bdcf688b 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-08-06 +date: 2024-08-09 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 7ad21a851bf9d..7626113758d05 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-08-06 +date: 2024-08-09 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 d68b2bbd3e171..0e487f02401b0 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-08-06 +date: 2024-08-09 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 6b37ccf753d63..9bbc0c2d082e4 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-08-06 +date: 2024-08-09 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 5d46ce3cd3fbd..134c6dc84e2d5 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-08-06 +date: 2024-08-09 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 57cdbe74dab90..51126cca22af6 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-08-06 +date: 2024-08-09 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 6a568c7aaaaca..0a4684c1fb556 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -15659,7 +15659,7 @@ "label": "GaugeCentralMajorMode", "description": [], "signature": [ - "\"none\" | \"auto\" | \"custom\"" + "\"none\" | \"custom\" | \"auto\"" ], "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", "deprecated": false, @@ -15689,7 +15689,7 @@ "label": "GaugeLabelMajorMode", "description": [], "signature": [ - "\"none\" | \"auto\" | \"custom\"" + "\"none\" | \"custom\" | \"auto\"" ], "path": "src/plugins/visualizations/common/convert_to_lens/types/configurations.ts", "deprecated": false, @@ -16105,7 +16105,7 @@ "label": "Operation", "description": [], "signature": [ - "\"min\" | \"max\" | \"sum\" | \"median\" | \"count\" | \"filters\" | \"range\" | \"average\" | \"date_histogram\" | \"percentile\" | \"terms\" | \"cumulative_sum\" | \"moving_average\" | \"unique_count\" | \"standard_deviation\" | \"percentile_rank\" | \"last_value\" | \"counter_rate\" | \"differences\" | \"formula\" | \"static_value\" | \"normalize_by_unit\"" + "\"min\" | \"max\" | \"sum\" | \"median\" | \"count\" | \"filters\" | \"terms\" | \"range\" | \"cumulative_sum\" | \"date_histogram\" | \"average\" | \"percentile\" | \"moving_average\" | \"unique_count\" | \"standard_deviation\" | \"percentile_rank\" | \"last_value\" | \"counter_rate\" | \"differences\" | \"formula\" | \"static_value\" | \"normalize_by_unit\"" ], "path": "src/plugins/visualizations/common/convert_to_lens/types/operations.ts", "deprecated": false, @@ -16135,7 +16135,7 @@ "label": "OperationWithSourceField", "description": [], "signature": [ - "\"min\" | \"max\" | \"sum\" | \"median\" | \"count\" | \"filters\" | \"range\" | \"average\" | \"date_histogram\" | \"percentile\" | \"terms\" | \"unique_count\" | \"standard_deviation\" | \"percentile_rank\" | \"last_value\"" + "\"min\" | \"max\" | \"sum\" | \"median\" | \"count\" | \"filters\" | \"terms\" | \"range\" | \"date_histogram\" | \"average\" | \"percentile\" | \"unique_count\" | \"standard_deviation\" | \"percentile_rank\" | \"last_value\"" ], "path": "src/plugins/visualizations/common/convert_to_lens/types/operations.ts", "deprecated": false, diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index cb92d920344b6..04dd0eba0cf18 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-08-06 +date: 2024-08-09 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/catalog-info.yaml b/catalog-info.yaml index 4af2698ca6cca..47fd1049e2fed 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -113,6 +113,9 @@ spec: metadata: name: kibana / sonarqube spec: + env: + SLACK_NOTIFICATIONS_CHANNEL: '#kibana-operations-alerts' + ELASTIC_SLACK_NOTIFICATIONS_ENABLED: 'true' repository: elastic/kibana provider_settings: trigger_mode: none diff --git a/config/serverless.yml b/config/serverless.yml index ba40d0fbb5712..6006b85323d32 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -108,8 +108,6 @@ xpack.index_management.editableIndexSettings: limited xpack.index_management.enableMappingsSourceFieldSection: false # Disable toggle for enabling data retention in DSL form from Index Management UI xpack.index_management.enableTogglingDataRetention: false -# Disable the Monaco migration in Console -console.dev.enableMonaco: false # Keep deeplinks visible so that they are shown in the sidenav dev_tools.deeplinks.navLinkStatus: visible diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 479987758b25a..d58ba5b97832f 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -74,7 +74,8 @@ Review important information about the {kib} 8.x releases. [[release-notes-8.15.0]] == {kib} 8.15.0 -coming::[8.15.0] + +For information about the {kib} 8.15.0 release, review the following information. [float] [[deprecations-8.15.0]] @@ -93,6 +94,278 @@ you make the necessary updates after you upgrade to 8.15.0. The Uptime app is already hidden from Kibana when there is no recent Heartbeat data, but will be completely removed in 9.0.0. You should migrate to Synthetics as an alternative. For more details, refer to the {observability-guide}/uptime-intro.html[Uptime documentation]. ==== +[float] +[[breaking-changes-8.15.0]] +=== Breaking changes + +Breaking changes can prevent your application from optimal operation and performance. +Before you upgrade to 8.15.0, review the breaking changes, then mitigate the impact to your application. + +[discrete] +[[breaking-184036]] +.Adds rate limiting to install by upload endpoint. +[%collapsible] +==== +*Details* + +Rate limiting was added to the upload `api/fleet/epm/packages` endpoint. For more information, refer to {kibana-pull}184036[#184036]. + +*Impact* + +If you do two or more requests in less than 10 seconds, the subsequent requests fail with `429 Too Many Requests`. +Wait 10 seconds before uploading again. +This change could potentially break automations for users that rely on frequent package uploads. +==== + +[float] +[[features-8.15.0]] +=== Features +{kib} 8.15.0 adds the following new and notable features. + +Alerting:: +* Allow decimals in the threshold filed for the Failed transaction rate threshold rule ({kibana-pull}184647[#184647]). +Cases:: +* Cases custom fields and the cases webhook are now GA ({kibana-pull}187880[#187880]). +* Allow users to create case using templates ({kibana-pull}187138[#187138]). +Dashboards:: +* Adding a panel to a dashboard now opens a flyout and the list of panels available is now organized more logically ({kibana-pull}183764[#183764]). +Discover:: +* In ES|QL mode, you can now create WHERE clause filters more intuitively by interacting with the table, sidebar and table row viewer, including for ordinal charts ({kibana-pull}181399[#181399]) & ({kibana-pull}184420[#184420]). +* You can now filter an ES|QL chart by brushing a date histogram ({kibana-pull}184012[#184012]). +Elastic Security:: +* For the Elastic Security 8.15.0 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Fleet:: +* UI for the custom integration creation with AI ({kibana-pull}186304[#186304]). +//* Change agent policies in edit package policy page ({kibana-pull}186084[#186084]). +//* Create shared package policy ({kibana-pull}185916[#185916]). +//* Introduce policy_ids in package policy SO ({kibana-pull}184636[#184636]). +* Surface option to delete diagnostics files ({kibana-pull}183690[#183690]). +* Allow to reset log level for agents >= 8.15.0 ({kibana-pull}183434[#183434]). +* Adds warning if need root integrations trying to be used with unprivileged agents ({kibana-pull}183283[#183283]). +* Adds unprivileged vs privileged agent count to Fleet UI ({kibana-pull}183077[#183077]). +Lens & Visualizations:: +* You can now show additional statistics in the legend of your time series charts created with *Lens* ({kibana-pull}182357[#182357]). +Machine Learning:: +* Adds Field statistics to the list of available panels in Dashboards ({kibana-pull}184030[#184030]). +* Adds ES|QL support for field statistics table in Discover ({kibana-pull}180849[#180849]). +* AIOps: Moves Pattern analysis to a tab instead of a flyout in Discover ({kibana-pull}178916[#178916]). +* AIOps: Adds AI Assistant contextual insights to the Log Rate Analysis page in the Machine Learning plugin for Observability serverless projects ({kibana-pull}186509[#186509]). +Management:: +* Adds an advanced `search:timeout` setting and changes the timeout behavior to display partial results instead of just an error ({kibana-pull}179679[#179679]). +Observability:: +* Updates links for integration buttons in Observability solution ({kibana-pull}184477[#184477]). +* Adds SLO status, SLI value, error budget remaining and consumed to the burn rate alert context ({kibana-pull}184471[#184471]). +* Adds an option to prevent initial backfill for SLOs ({kibana-pull}184312[#184312]). +* Updates `Add data` links to use improved deep linking ({kibana-pull}184164[#184164]). +* Changes the navigation behavior for the Observability guide cards to pre-select the correct solution ({kibana-pull}184065[#184065]). +* Updates the Alerts details to now show an history chart for all types of alerts ({kibana-pull}181824[#181824]). +* Adds Docs count, Size, Services, Hosts, and Degraded docs KPIs to the dataset quality flyout ({kibana-pull}179479[#179479]). +Platform:: +* Improves the **Share** menu to let you navigate through a tabbed modal to copy links for Discover, Dashboards, and Lens. ({kibana-pull}180406[#180406]). +* Changes the behavior of the "Endpoints and API keys" button in the header to open the Connection details flyout ({kibana-pull}183236[#183236]). +* Adds a quick way to create an API keys with a 90-days expiration to the Connection details flyout, and clarifies the Elasticsearch endpoint and Cloud ID information ({kibana-pull}180912[#180912]). +* Adds a feedback button to the header in Serverless projects({kibana-pull}180942[#180942]). + +For more information about the features introduced in 8.15.0, refer to <>. + +[[enhancements-and-bug-fixes-v8.15.0]] +=== Enhancements and bug fixes + +For detailed information about the 8.15.0 release, review the enhancements and bug fixes. + + +[float] +[[enhancement-v8.15.0]] +=== Enhancements +Alerting:: +* Adds support of additional fields for ServiceNow ITSM and SecOps ({kibana-pull}184023[#184023]). +* Adds support for the additional info field in the ServiceNow ITOM connector ({kibana-pull}183380[#183380]). +Cases:: +* The Cases webhook connector now supports SSL certificate authentication ({kibana-pull}185925[#185925]). +Dashboards:: +* Adds a "Creator" column to the Dashboards list ({kibana-pull}182256[#182256]). +* Adds the ability to filter dashboards by creator for dashboards created on or after version 8.14 ({kibana-pull}180147[#180147]). +* When switching back from maximized to minimized view on a panel, you return to your original position in the dashboard ({kibana-pull}184696[#184696]). +* Improves the dashboard background color to match the current color mode wen margins are turned off ({kibana-pull}181450[#181450]). +* Simplifies the workflow for creating a copy of the dashboard currently open in both view and edit modes ({kibana-pull}180938[#180938]). +Discover:: +* Adds a button to expand the time range on demand when no results are found for a search ({kibana-pull}181723[#181723]). +* Changes the Discover document viewer flyout to push the rest of the UI instead of hiding it with an overlay ({kibana-pull}166406[#166406]). +* CSV reports now include custom field labels when they exist ({kibana-pull}181565[#181565]). +Elastic Security:: +* For the Elastic Security 8.15.0 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +ES|QL:: +* Improves metadata autocomplete suggestions with comma and pipe ({kibana-pull}188338[#188338]). +* Automatically encapsulate index names with special characters with quotes ({kibana-pull}187899[#187899]). +* Adds support for integrations ({kibana-pull}184716[#184716]). +* Adds the ability to comment out the current line with the keyboard using `CMD + /` ({kibana-pull}184637[#184637]). + +Fleet:: +* Use API key for standalone agent onboarding ({kibana-pull}187133[#187133]). +//* Adds action for upgrading all agents on policy ({kibana-pull}186827[#186827]). +* Makes Fleet & Integrations layouts full width ({kibana-pull}186056[#186056]). +* Adds support for setting `add_fields` processors on all agents under an agent policy ({kibana-pull}184693[#184693]). +* Adds force flag to delete agent_policies API ({kibana-pull}184419[#184419]). +* Adds data tags to agent policy APIs ({kibana-pull}183563[#183563]). +* Adds support for mappings with store: true ({kibana-pull}183390[#183390]). +* Shows all integration assets on detail page ({kibana-pull}182180[#182180]). +* Adds overrides to package policies update endpoint ({kibana-pull}181453[#181453]). +* Enables `agent.monitoring.http` settings on agent policy UI ({kibana-pull}180922[#180922]). +* Removes unnecessary field definitions for custom integrations and adds `logs@mappings` to log streams ({kibana-pull}178083[#178083]). +Lens & Visualizations:: +* Adds wildcard matching to field pickers across {kib} in *Lens* ({kibana-pull}182631[#182631]). +Machine Learning:: +* AIOps: Adds cardinality check to Log Rate Analysis ({kibana-pull}181129[#181129]). +* AIOps: Reduces rerenders when streaming analysis results ({kibana-pull}182793[#182793]). +* AIOps Log Rate Analysis: Improves explanation of log rate spike/dip ({kibana-pull}186342[#186342]). +* AIOps Log Rate Analysis: Merges fetch queue for keyword and text field candidates ({kibana-pull}183649[#183649]). +* AIOps Log Rate Analysis: Adds controls for controlling which columns will be visible ({kibana-pull}184262[#184262]). +* Anomaly Detection: Single Metric Viewer - Adds cases action ({kibana-pull}183423[#183423]). +* Anomaly Detection: Adds 'Add to dashboard' action for Single Metric Viewer ({kibana-pull}182538[#182538]). +* Anomaly swim lane: UX improvements ({kibana-pull}182586[#182586]). +* Single Metric Viewer embeddable: Ensures chart height is responsive on resize ({kibana-pull}185907[#185907]). +* Single Metric Viewer embeddable in dashboards: Moves all config to flyout ({kibana-pull}182756[#182756]). +* Adds progress bar for trained models download ({kibana-pull}184906[#184906]). +* Updates code editors for Transform, Data Frame and Anomaly Detection wizards ({kibana-pull}184518[#184518]). +Management:: +* The SIEM Query rule loads fewer fields on query execution ({kibana-pull}184890[#184890]). +* The ES Query rule loads fewer fields on query execution ({kibana-pull}183694[#183694]). +* Adds the ability to horizontally resize the autocomplete popup in the Dev Tools Console ({kibana-pull}180243[#180243]). +* The Kibana configuration file now supports assigning a default value for environment variables, using the `${VAR_ENV:defaultValue}` syntax. ({kibana-pull}182139[#182139]). +Observability:: +* Adds the ability to clone a monitor in Synthetics ({kibana-pull}184393[#184393]). +* Adds 10 and 30 seconds frequency options to lightweight monitors in Synthetics ({kibana-pull}184380[#184380]). +* Improves the destination of `Add data` links in Observability to make data ingestion more efficient ({kibana-pull}184164[#184164]). +* Adds an option to mark AI Assistant Knowledge Base entries as public ({kibana-pull}184094[#184094]). +* Updates the AI assistant to query all search connectors by default and adds a setting to override this new default ({kibana-pull}183712[#183712]). +* Adds downstream dependency service name to logs and errors to improve alert insights ({kibana-pull}183215[#183215]). +Operations:: +* Adds password support to the Kibana keystore ({kibana-pull}180414[#180414]). +Platform:: +* Adds http2 support to the Kibana server, that can be enabled using the `server.protocol: http2` Kibana setting ({kibana-pull}183465[#183465]). +* Kibana's `rolling-file` appender now supports more advanced retention policies. Refer to the <> for more details ({kibana-pull}182346[#182346]). +Security:: +* Adds an optional role description field to roles ({kibana-pull}183145[#183145]). +* Adds the ability to filter audit logs by username using the `xpack.security.audit.ignore_filters.users` configuration setting ({kibana-pull}183137[#183137]). +* Adds support for `remote_cluster` privileges in ES role definition ({kibana-pull}182377[#182377]). +* Improves the experience for managing a larger number of API keys by adding server side filtering, pagination and querying. ({kibana-pull}168970[#168970]). + +[float] +[[fixes-v8.15.0]] +=== Bug Fixes +Alerting:: +* Fixes kibana.alert.rule.execution.timestamp timezone and format ({kibana-pull}183905[#183905]). +* Fixes undefined error source in alerting log tags ({kibana-pull}182352[#182352]). +* Allow the rule types to throw user errors ({kibana-pull}184213[#184213]). +* Sets validation errors in subaction framework as user errors ({kibana-pull}184317[#184317]). +* Fixes x-axis time zone on alertSummaryWidget full size ({kibana-pull}187468[#187468]). +Dashboards:: +* Prevent jumping control drag handle between view modes ({kibana-pull}184533[#184533]). +* Fixes error on navigation when invalid selections tour step is open ({kibana-pull}189449[#189449]). +* Fixes positioning of dragged link in Links editor ({kibana-pull}189122[#189122]). +* Don't close the flyout when canceling the Save to library action ({kibana-pull}188995[#188995]). +* Fixes error thrown on numeric options list ({kibana-pull}188789[#188789]). +* Adds tooltip support to `PresentationPanel` header badges ({kibana-pull}186102[#186102]). +* Fixes unsaved changes on new dashboards bug ({kibana-pull}184955[#184955]). +* Reset `maximizedPanelId` on Dashboard navigation ({kibana-pull}183060[#183060]). +Discover:: +* Fixes time range filter ({kibana-pull}187010[#187010]). +* Reset selected fields when modifying the ES|QL query ({kibana-pull}185997[#185997]). +* Fixes document comparison mode and the field statistics tab when using Smart Fields ({kibana-pull}184172[#184172]). +* Disables sorting for Document view ({kibana-pull}187553[#187553]). +* Correctly adds the limit to the field statistics queries ({kibana-pull}186967[#186967]). +* Fixes overlapping on error messages ({kibana-pull}181416[#181416]). +Elastic Search:: +* Allow to save mappings with errors ({kibana-pull}188326[#188326]). + +Elastic Security:: +* For the Elastic Security 8.15.0 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +ES|QL:: +* Suppress empty syntax error ({kibana-pull}184246[#184246]). +* Accept null values for function arguments ({kibana-pull}184254[#184254]). +* Recognize transformational commands with ast parsing ({kibana-pull}184291[#184291]). +* Accept negated index patterns ({kibana-pull}184528[#184528]). +* Fixes the overflow problem of the editor ({kibana-pull}186166[#186166]). +* Removes inaccurate value suggestions ({kibana-pull}189228[#189228]). +* Improves support for `Invoke` completion trigger type ({kibana-pull}188877[#188877]). +Fleet:: +* Fixes navigating back to Agent policy integration list ({kibana-pull}189165[#189165]). +* Fixes copy agent policy, missed bump revision ({kibana-pull}188935[#188935]). +* Force field enabled=false on inputs that have all their streams disabled ({kibana-pull}188919[#188919]). +* Fill in empty values for `constant_keyword` fields from existing mappings ({kibana-pull}188145[#188145]). +* Enrollment token table may show an empty last page ({kibana-pull}188049[#188049]). +* Separated `showInactive` from unenrolled status filter ({kibana-pull}187960[#187960]). +* Fixes missing policy filter in Fleet Server check to enable secrets ({kibana-pull}187935[#187935]). +* Allow preconfigured agent policy only with name and id ({kibana-pull}187542[#187542]). +* Show warning callout in configs tab when an error occurs ({kibana-pull}187487[#187487]). +* Enable rollover in custom integrations install when getting mapper_exception error ({kibana-pull}186991[#186991]). +* Adds concurrency limit to EPM bulk install API + fix duplicate installations ({kibana-pull}185900[#185900]). +* Include inactive agents in agent policy agent count ({kibana-pull}184517[#184517]). +* Fixes KQL filtering ({kibana-pull}183757[#183757]). +* Prevent concurrent runs of Fleet setup ({kibana-pull}183636[#183636]). +Lens & Visualizations:: +* Do not pass incorrect filters to the state in *Lens* ({kibana-pull}189292[#189292]). +* Adds error reason in workspace panel when error happens in *Lens* ({kibana-pull}189161[#189161]). +* Fixes "Unable to load" page error on edit/add ES|QL panel ({kibana-pull}188664[#188664]). +* Improves the performance of the table ES|QL visualization ({kibana-pull}187142[#187142]). +* Fixes sort field error message for last value ({kibana-pull}184883[#184883]). +* Fixes reference line width stale update in *Lens* ({kibana-pull}184414[#184414]). +* Fixes prepend sizing on duration formatter in *Lens* ({kibana-pull}184403[#184403]). +* Fixes data table actions when the first row is empty in *Lens* ({kibana-pull}181344[#181344]). +* Fixes y-axis scale/custom domain issues and help/error text in *Lens* ({kibana-pull}180532[#180532]). +Logs:: +* Fixes log stream flyout when embedded in APM or the Infrastructure UI ({kibana-pull}189763[#189763]). +* Fixes log entry flyout when response is slow ({kibana-pull}187303[#187303]). +* Fixes flyout link to the legacy Uptime app ({kibana-pull}186328[#186328]). +Machine Learning:: +* Fixes display of model state in trained models list with starting and stopping deployments ({kibana-pull}188847[#188847]). +* AIOps: Fixes runtime mappings in pattern analysis ({kibana-pull}188530[#188530]). +* Fixes Field statistics panel displaying multiple errors if associated index is deleted and race condition when refreshes too fast ({kibana-pull}188327[#188327]). +* Hides ML embeddables from the "Add panel" flyout when ML isn't available ({kibana-pull}187639[#187639]). +* Removes info callout mentioning ML nodes for serverless environment ({kibana-pull}187583[#187583]). +* Fixes upgrade warning ({kibana-pull}187387[#187387]). +* Fixes change point menu which can get stuck open ({kibana-pull}186063[#186063]). +* Do not retry model deployment ({kibana-pull}185012[#185012]). +* Refreshes jobs list after import ({kibana-pull}184757[#184757]). +* AIOps Log Rate Analysis: Fixes date picker refresh button ({kibana-pull}183768[#183768]). +* Single Metric Viewer embeddable: Ensures creating job rule from anomaly click actions is successful ({kibana-pull}183554[#183554]). +* Adds bucket span validation to job creation flyouts ({kibana-pull}183510[#183510]). +* Fixes Choropleth map disappears when time range is changed ({kibana-pull}181933[#181933]). +Management:: +* Allows selection of timestamp when some index pattern segments are unmet ({kibana-pull}189336[#189336]). +* Transforms and Anomaly detection: Updates width for icon in messages tab to prevent overlap ({kibana-pull}188374[#188374]). +* Transform: Fixes transform stats API call in the transform health alerting rule ({kibana-pull}187586[#187586]). +* Transforms: Improves data view checks ({kibana-pull}181892[#181892]). +* Fixes human-readable (precise) formatting ({kibana-pull}181391[#181391]). +Observability:: +* Decreases bucket size top_dependencies sends to get_connection_stats query ({kibana-pull}182884[#182884]). +* Fixes SLO history details data ({kibana-pull}183097[#183097]). +* Improves permission check on SLO pages ({kibana-pull}182609[#182609]). +* Fixes contextual insights for APM errors ({kibana-pull}184642[#184642]). +* Fixes Alerts page history navigation ({kibana-pull}186068[#186068]). +* Accept project monitors with `monitor.url` of type `string` that contains commas ({kibana-pull}186112[#186112]). +* Fixes TLS certificate view for > 3 monitors per certificate ({kibana-pull}186204[#186204]). +* Fixes Synthetics/Uptime fields alerts autocomplete for query bar ({kibana-pull}186588[#186588]). +* Hides AI Assistant menu item when in a disabled space ({kibana-pull}188017[#188017]). +* Fixes AI Assistant settings when plugin is disabled ({kibana-pull}188160[#188160]). +* Fixes bug “Cannot set initialMessages if initialConversationId is set" ({kibana-pull}189885[#189885]). +* Respect query:allowLeadingWildcards in optional query filter ({kibana-pull}189488[#189488]). +* Fixes showing the correct log view in the rule creation flyout ({kibana-pull}189205[#189205]). +* Fixes a bug where "Retest on failure" couldn't be turned off when creating a monitor in the Synthetics app. ({kibana-pull}189013[#189013]). +* Fixes incorrect Redis and AWS CPU percentage metrics displayed on the Infrastructure Inventory page ({kibana-pull}188768[#188768]). +* Improves information communicated in case of insufficient privileges in the Dataset Quality UI ({kibana-pull}183947[#183947]). +Platform:: +* Accessibility fixes for user profile input labels ({kibana-pull}186471[#186471]). +* Fixes several internationalization and localization inconsistencies ({kibana-pull}181735[#181735]). +* Fixes case sensitivity in tag search ({kibana-pull}183092[#183092]). +Querying & Filtering:: +* Fixes performance issues with nested sub-queries ({kibana-pull}181208[#181208]). +Security:: +* Fixes `ComboBox` overflow with large chips ({kibana-pull}184722[#184722]). +* Adds `disabledFeatures` back to mappings, so it can be aggregated on ({kibana-pull}184195[#184195]). +* Supports read-only remote index and cluster sections with read_security access ({kibana-pull}183126[#183126]). +* Adds transformation of application wildcard `*` privilege to `all` to correctly filter and display roles as `superuser`. ({kibana-pull}181400[#181400]). +Sharing:: +* Improves error handling ({kibana-pull}185903[#185903]). + [[release-notes-8.14.3]] == {kib} 8.14.3 @@ -3853,7 +4126,7 @@ Review the following information about the {kib} 8.6.0 release. When you attempt to create an APM latency threshold rule in **Observability** > **Alerts** > **Rules** for all services or all transaction types, the request will fail with a `params invalid` error. *Impact* + -This known issue only impacts the Observability Rules page. To work around this issue, create APM latency threshold rules in the APM Alerts and Rules dialog. See {kibana-ref}/apm-alerts.html[Alerts and rules] for detailed instructions. +This known issue only impacts the Observability Rules page. To work around this issue, create APM latency threshold rules in the APM Alerts and Rules dialog. See {observability-guide}/apm-alerts.html[Alerts and rules] for detailed instructions. ==== [float] @@ -5443,7 +5716,7 @@ you make the necessary updates after you upgrade to 8.3.0. Removes the `apm_user` role. For more information, check {kibana-pull}132790[#132790]. *Impact* + -In the link:https://www.elastic.co/guide/en/kibana/8.3/xpack-apm.html[APM documentation], the `apm_user`role is replaced with the `viewer` and `editor` built-in roles. +The `apm_user`role is replaced with the `viewer` and `editor` built-in roles. ==== [discrete] @@ -7629,7 +7902,7 @@ Use spaces, cross-cluster replication, or cross-cluster search. To migrate to << The logging configuration and log output format has changed. For more information, refer to {kibana-pull}112305[#112305]. *Impact* + -Use the new <>. +Use the new <>. ==== [float] diff --git a/docs/api/role-management.asciidoc b/docs/api/role-management.asciidoc index 4c4620a23943a..7fbded3e57dd3 100644 --- a/docs/api/role-management.asciidoc +++ b/docs/api/role-management.asciidoc @@ -9,6 +9,7 @@ WARNING: Do not use the {ref}/security-api.html#security-role-apis[{es} role man The following {kib} role management APIs are available: * <> to create a new {kib} role, or update the attributes of an existing role +* <> to create a new {kib} roles, or update the attributes of existing roles * <> to retrieve all {kib} roles @@ -20,3 +21,4 @@ include::role-management/put.asciidoc[] include::role-management/get.asciidoc[] include::role-management/get-all.asciidoc[] include::role-management/delete.asciidoc[] +include::role-management/put-bulk.asciidoc[] diff --git a/docs/api/role-management/put-bulk.asciidoc b/docs/api/role-management/put-bulk.asciidoc new file mode 100644 index 0000000000000..e41b836e26357 --- /dev/null +++ b/docs/api/role-management/put-bulk.asciidoc @@ -0,0 +1,377 @@ +[[role-management-api-put-bulk]] +=== Bulk create or update roles API +++++ +Bulk create or update roles API +++++ + +preview::["This functionality is in technical preview, and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features."] + +experimental[] Create new {kib} roles, or update the attributes of an existing roles. {kib} roles are stored in the +{es} native realm. + +[[role-management-api-put-bulk-request]] +==== Request + +`POST :/api/security/roles` + +[[role-management-api-put-bulk-prereqs]] +==== Prerequisite + +To use the bulk create or update roles API, you must have the `manage_security` cluster privilege. + +[role="child_attributes"] +[[role-management-api-bulk-response-body]] +==== Request body + +`roles`:: + (object) Object that specifies the roles to add as a role name to role map. +`` (required):: (string) The role name. +`description`:: + (Optional, string) Description for the role. + +`metadata`:: + (Optional, object) In the `metadata` object, keys that begin with `_` are reserved for system usage. + +`elasticsearch`:: + (Optional, object) {es} cluster and index privileges. Valid keys include + `cluster`, `indices`, `remote_indices`, `remote_cluster`, and `run_as`. For more information, see + {ref}/defining-roles.html[Defining roles]. + +`kibana`:: + (list) Objects that specify the <> for the role. ++ +.Properties of `kibana` +[%collapsible%open] +===== +`base` ::: + (Optional, list) A base privilege. When specified, the base must be `["all"]` or `["read"]`. + When the `base` privilege is specified, you are unable to use the `feature` section. + "all" grants read/write access to all {kib} features for the specified spaces. + "read" grants read-only access to all {kib} features for the specified spaces. + +`feature` ::: + (object) Contains privileges for specific features. + When the `feature` privileges are specified, you are unable to use the `base` section. + To retrieve a list of available features, use the <>. + +`spaces` ::: + (list) The spaces to apply the privileges to. + To grant access to all spaces, set to `["*"]`, or omit the value. +===== + +[[role-management-api-bulk-put-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Examples + +Grant access to various features in all spaces: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/security/roles +{ + "roles": { + "my_kibana_role_1": { + "description": "my_kibana_role_1_description", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": [], + "feature": { + "discover": ["all"], + "visualize": ["all"], + "dashboard": ["all"], + "dev_tools": ["read"], + "advancedSettings": ["read"], + "indexPatterns": ["read"], + "graph": ["all"], + "apm": ["read"], + "maps": ["read"], + "canvas": ["read"], + "infrastructure": ["all"], + "logs": ["all"], + "uptime": ["all"] + }, + "spaces": ["*"] + } + ] + }, + "my_kibana_role_2": { + "description": "my_kibana_role_2_description", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": [], + "feature": { + "discover": ["all"], + "visualize": ["all"], + "dashboard": ["all"], + "dev_tools": ["read"], + "logs": ["all"], + "uptime": ["all"] + }, + "spaces": ["*"] + } + ] + } + } +} +-------------------------------------------------- +// KIBANA + +Grant dashboard-only access to only the Marketing space for `my_kibana_role_1` and dashboard-only access to only Sales space for `my_kibana_role_2`: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/security/roles +{ + "roles": { + "my_kibana_role_1": { + "description": "Grants dashboard-only access to only the Marketing space.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": [], + "feature": { + "dashboard": ["read"] + }, + "spaces": ["marketing"] + } + ] + }, + "my_kibana_role_2": { + "description": "Grants dashboard-only access to only the Sales space.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": [], + "feature": { + "dashboard": ["read"] + }, + "spaces": ["sales"] + } + ] + } + } +} + +-------------------------------------------------- +// KIBANA + +Grant full access to all features in the Default space for `my_kibana_role_1` and `my_kibana_role_2`: + +[source,sh] +-------------------------------------------------- +$ curl -X PUT api/security/role +{ + "roles": { + "my_kibana_role_1": { + "description": "Grants full access to all features in the Default space.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": ["all"], + "feature": {}, + "spaces": ["default"] + } + ] + }, + "my_kibana_role_2": { + "description": "Grants full access to all features in the Default space.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": ["all"], + "feature": {}, + "spaces": ["default"] + } + ] + } + } +} + +-------------------------------------------------- +// KIBANA + +Grant different access to different spaces: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/security/roles +{ + "roles": { + "my_kibana_role_1": { + "description": "Grants full access to discover and dashboard features in the default space. Grants read access in the marketing, and sales spaces.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": [], + "feature": { + "discover": ["all"], + "dashboard": ["all"] + }, + "spaces": ["default"] + }, + { + "base": ["read"], + "spaces": ["marketing", "sales"] + } + ] + }, + "my_kibana_role_2": { + "description": "Grants full access to discover and dashboard features in the default space. Grants read access in the marketing space.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": [], + "indices": [] + }, + "kibana": [ + { + "base": [], + "feature": { + "discover": ["all"], + "dashboard": ["all"] + }, + "spaces": ["default"] + }, + { + "base": ["read"], + "spaces": ["marketing"] + } + ] + } + } +} + +-------------------------------------------------- +// KIBANA + +Grant access to {kib} and {es}: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/security/roles +{ + "roles": { + "my_kibana_role_1": { + "description": "Grants all cluster privileges and full access to index1 and index2. Grants full access to remote_index1 and remote_index2, and the monitor_enrich cluster privilege on remote_cluster1. Grants all Kibana privileges in the default space.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": ["all"], + "indices": [ + { + "names": ["index1", "index2"], + "privileges": ["all"] + } + ], + "remote_indices": [ + { + "clusters": ["remote_cluster1"], + "names": ["remote_index1", "remote_index2"], + "privileges": ["all"] + } + ], + "remote_cluster": [ + { + "clusters": ["remote_cluster1"], + "privileges": ["monitor_enrich"] + } + ] + }, + "kibana": [ + { + "base": ["all"], + "feature": {}, + "spaces": ["default"] + } + ] + }, + "my_kibana_role_2": { + "description": "Grants all cluster privileges and full access to index1. Grants full access to remote_index1, and the monitor_enrich cluster privilege on remote_cluster1. Grants all Kibana privileges in the default space.", + "metadata": { + "version": 1 + }, + "elasticsearch": { + "cluster": ["all"], + "indices": [ + { + "names": ["index1"], + "privileges": ["all"] + } + ], + "remote_indices": [ + { + "clusters": ["remote_cluster1"], + "names": ["remote_index1"], + "privileges": ["all"] + } + ], + "remote_cluster": [ + { + "clusters": ["remote_cluster1"], + "privileges": ["monitor_enrich"] + } + ] + }, + "kibana": [ + { + "base": ["all"], + "feature": {}, + "spaces": ["default"] + } + ] + } + } +} + +-------------------------------------------------- +// KIBANA diff --git a/docs/apm/advanced-queries.asciidoc b/docs/apm/advanced-queries.asciidoc deleted file mode 100644 index c74615f40a647..0000000000000 --- a/docs/apm/advanced-queries.asciidoc +++ /dev/null @@ -1,85 +0,0 @@ -[role="xpack"] -[[advanced-queries]] -=== Query your data - -Querying your APM data is an essential tool that can make finding bottlenecks in your code even more straightforward. - -Using the query bar, a powerful data query feature, you can pass advanced queries on your data -to filter on specific pieces of information you’re interested in. - -The query bar comes with a handy autocomplete that helps find the fields and even provides suggestions to the data they include. -You can select the query bar and hit the down arrow on your keyboard to begin scanning recommendations. - -[float] -[[apm-app-advanced-queries]] -=== Querying in the APM app - -When querying in the APM app, you’re merely searching and selecting data from fields in {es} documents. Queries entered -into the query bar are also added as parameters to the URL, so it’s easy to share a specific query or view with others. - -When you type, you can begin to see some of the transaction fields available for filtering: - -[role="screenshot"] -image::apm/images/apm-query-bar.png[Example of the Kibana Query bar in APM app in Kibana] - -[TIP] -===== -To learn more about the {kib} query language capabilities, see the {kibana-ref}/kuery-query.html[Kibana Query Language Enhancements] documentation. -===== - -[float] -[[apm-app-queries]] -==== APM app queries - -APM queries can be handy for removing noise from your data in the <>, <>, -<>, <>, and <> views. - -For example, in the *Services* view, you can quickly view a list of all the instrumented services running on your production -environment: `service.environment : production`. Or filter the list by including the APM agent's name and the host it’s running on: -`service.environment : "production" and agent.name : "java" and host.name : "prod-server1"`. - -On the *Traces* view, you might want to view failed transaction results from any of your running containers: -`transaction.result :"FAILURE" and container.id : *`. - -On the *Transactions* view, you may want to list only the slower transactions than a specified time threshold: `transaction.duration.us > 2000000`. -Or filter the list by including the service version and the Kubernetes pod it's running on: -`transaction.duration.us > 2000000 and service.version : "7.12.0" and kubernetes.pod.name : "pod-5468b47f57-pqk2m"`. - -[float] -[[discover-advanced-queries]] -=== Querying in Discover - -Alternatively, you can query your APM documents in {kibana-ref}/discover.html[*Discover*]. -Querying documents in *Discover* works the same way as queries in the APM app, -and *Discover* supports all of the example APM app queries shown on this page. - -[float] -[[discover-queries]] -==== Discover queries - -One example where you may want to make use of *Discover* -is to view _all_ transactions for an endpoint instead of just a sample. - -TIP: Starting in v7.6, you can view ten samples per bucket in the APM app, instead of just one. - -Use the APM app to find a transaction name and time bucket that you're interested in learning more about. -Then, switch to *Discover* and make a search: - -["source","sh"] ------ -processor.event: "transaction" AND transaction.name: "" and transaction.duration.us > 13000 and transaction.duration.us < 14000` ------ - -In this example, we're interested in viewing all of the `APIRestController#customers` transactions -that took between 13 and 14 milliseconds. Here's what Discover returns: - -[role="screenshot"] -image::apm/images/advanced-discover.png[View all transactions in bucket] - -You can now explore the data until you find a specific transaction that you're interested in. -Copy that transaction's `transaction.id` and paste it into the APM app to view the data in the context of the APM app: - -[role="screenshot"] -image::apm/images/specific-transaction-search.png[View specific transaction in apm app] -[role="screenshot"] -image::apm/images/specific-transaction.png[View specific transaction in apm app] diff --git a/docs/apm/agent-configuration.asciidoc b/docs/apm/agent-configuration.asciidoc deleted file mode 100644 index 62d4f3d7e2fec..0000000000000 --- a/docs/apm/agent-configuration.asciidoc +++ /dev/null @@ -1,56 +0,0 @@ -[role="xpack"] -[[agent-configuration]] -=== APM Agent central configuration - -++++ -Configure APM agents with central config -++++ - -APM Agent configuration allows you to fine-tune your APM agent configuration from within the APM app. -Changes are automatically propagated to your APM agents, so there's no need to redeploy. - -To get started, choose the services and environments you wish to configure. -The APM app will let you know when your APM agents have applied your configurations. - -[role="screenshot"] -image::apm/images/apm-agent-configuration.png[APM Agent configuration in Kibana] - -[float] -==== Precedence - -Configurations set from the APM app take precedence over configurations set locally in each APM agent. -However, if APM Server is slow to respond, is offline, reports an error, etc., -APM agents will use local defaults until they're able to update the configuration. -For this reason, it is still essential to set custom default configurations locally in each of your APM agents. - -[float] -==== Supported configurations - -Each APM agent has a list of supported configurations. -After selecting a Service name and environment in the APM app, -a list of all supported configuration options, -including descriptions and default values, will be displayed. - -Supported configurations are also tagged with the image:./images/dynamic-config.svg[] badge in each APM agent's configuration reference: - -[horizontal] -Android agent:: {apm-android-ref}/configuration.html[Configuration reference] -Go agent:: {apm-go-ref}/configuration.html[Configuration reference] -iOS agent:: {apm-ios-ref}/configuration.html[Configuration reference] -Java agent:: {apm-java-ref}/configuration.html[Configuration reference] -.NET agent:: {apm-dotnet-ref}/configuration.html[Configuration reference] -Node.js agent:: {apm-node-ref}/configuration.html[Configuration reference] -PHP agent:: {apm-php-ref}/configuration.html[Configuration reference] -Python agent:: {apm-py-ref}/configuration.html[Configuration reference] -Ruby agent:: {apm-ruby-ref}/configuration.html[Configuration reference] -Real User Monitoring (RUM) agent:: {apm-rum-ref}/configuration.html[Configuration reference] - -[float] -==== APM Server configuration - -For most users, APM agent configuration should work out-of-the-box. -If you run into trouble, it may be because you're not using the {es} output, -or because your {es} credentials don't have sufficient privileges. - -See {apm-guide-ref}/configure-agent-config.html[configure APM agent configuration] -to learn how to configure APM Server to avoid these problems. diff --git a/docs/apm/agent-explorer.asciidoc b/docs/apm/agent-explorer.asciidoc deleted file mode 100644 index 2129853c9ee8c..0000000000000 --- a/docs/apm/agent-explorer.asciidoc +++ /dev/null @@ -1,18 +0,0 @@ -[[agent-explorer]] -=== APM Agent explorer - -++++ -Identify deployment details for APM agents -++++ - -beta::[] - -APM agent explorer provides a centralized panel to identify APM agent deployment details, like service name, environment, instances, and agent name, version, and documentation. - -[role="screenshot"] -image::apm/images/apm-agent-explorer.png[APM agent explorer] - -Select an APM agent to expand it and view the details of each agent instance. - -[role="screenshot"] -image::apm/images/apm-agent-explorer-flyout.png[APM agent explorer flyout] diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc deleted file mode 100644 index 113a03437cbef..0000000000000 --- a/docs/apm/api.asciidoc +++ /dev/null @@ -1,826 +0,0 @@ -[role="xpack"] -[[apm-api]] -== APM app API - -++++ -REST API -++++ - -Some APM app features are provided via a REST API: - -* <> -* <> -* <> -* <> - -[float] -[[apm-api-example]] -=== Using the APIs - -// The following content is reused throughout the API docs -// tag::using-the-APIs[] -Interact with APM APIs using cURL or another API tool. -All APM APIs are Kibana APIs, not Elasticsearch APIs; -because of this, the Kibana dev tools console cannot be used to interact with APM APIs. - -For all APM APIs, you must use a request header. -Supported headers are `Authorization`, `kbn-xsrf`, and `Content-Type`. - -`Authorization: ApiKey {credentials}`:: -Kibana supports token-based authentication with the Elasticsearch API key service. -The API key returned by the {ref}/security-api-create-api-key.html[Elasticsearch create API key API] -can be used by sending a request with an `Authorization` header that has a value of `ApiKey` followed by the `{credentials}`, -where `{credentials}` is the base64 encoding of `id` and `api_key` joined by a colon. -+ -Alternatively, you can create a user and use their username and password to authenticate API access: `-u $USER:$PASSWORD`. -+ -Whether using `Authorization: ApiKey {credentials}`, or `-u $USER:$PASSWORD`, -users interacting with APM APIs must have <>. - -`kbn-xsrf: true`:: - By default, you must use `kbn-xsrf` for all API calls, except in the following scenarios: - -* The API endpoint uses the `GET` or `HEAD` operations -* The path is allowed using the <> setting -* XSRF protections are disabled using the <> setting - -`Content-Type: application/json`:: - Applicable only when you send a payload in the API request. - {kib} API requests and responses use JSON. - Typically, if you include the `kbn-xsrf` header, you must also include the `Content-Type` header. -// end::using-the-APIs[] - -Here's an example CURL request that adds an annotation to the APM app: - -[source,curl] ----- -curl -X POST \ - http://localhost:5601/api/apm/services/opbeans-java/annotation \ --H 'Content-Type: application/json' \ --H 'kbn-xsrf: true' \ --H 'Authorization: Basic YhUlubWZhM0FDbnlQeE6WRtaW49FQmSGZ4RUWXdX' \ --d '{ - "@timestamp": "2020-05-11T10:31:30.452Z", - "service": { - "version": "1.2" - }, - "message": "Revert upgrade", - "tags": [ - "elastic.co", "customer" - ] - }' ----- - -[float] -[[kibana-api]] -=== Kibana API - -In addition to the APM specific API endpoints, Kibana provides its own <> -which you can use to automate certain aspects of configuring and deploying Kibana. - -//// -******************************************************* -******************************************************* -//// - -[role="xpack"] -[[agent-config-api]] -=== Agent Configuration API - -The APM agent configuration API allows you to fine-tune your APM agent configuration, -without needing to redeploy your application. - -The following APM agent configuration APIs are available: - -* <> to create or update an APM agent configuration -* <> to delete an APM agent configuration. -* <> to list all APM agent configurations. -* <> to search for an APM agent configuration. - -[float] -[[use-agent-config-api]] -==== How to use APM APIs - -.Expand for required headers, privileges, and usage details -[%collapsible%closed] -====== -include::api.asciidoc[tag=using-the-APIs] -====== - -//// -******************************************************* -//// - -[[apm-update-config]] -==== Create or update configuration - -[[apm-update-config-req]] -===== Request - -`PUT /api/apm/settings/agent-configuration` - -[role="child_attributes"] -[[apm-update-config-req-body]] -===== Request body - -`service`:: -(required, object) Service identifying the configuration to create or update. -+ -.Properties of `service` -[%collapsible%open] -====== -`name` ::: - (required, string) Name of service - -`environment` ::: - (optional, string) Environment of service -====== - -`settings`:: -(required) Key/value object with option name and option value. - -`agent_name`:: -(optional) The agent name is used by the UI to determine which settings to display. - - -[[apm-update-config-example]] -===== Example - -[source,curl] --------------------------------------------------- -PUT /api/apm/settings/agent-configuration -{ - "service": { - "name": "frontend", - "environment": "production" - }, - "settings": { - "transaction_sample_rate": "0.4", - "capture_body": "off", - "transaction_max_spans": "500" - }, - "agent_name": "nodejs" -} --------------------------------------------------- - -//// -******************************************************* -//// - - -[[apm-delete-config]] -==== Delete configuration - -[[apm-delete-config-req]] -===== Request - -`DELETE /api/apm/settings/agent-configuration` - -[role="child_attributes"] -[[apm-delete-config-req-body]] -===== Request body -`service`:: -(required, object) Service identifying the configuration to delete -+ -.Properties of `service` -[%collapsible%open] -====== -`name` ::: - (required, string) Name of service - -`environment` ::: - (optional, string) Environment of service -====== - - -[[apm-delete-config-example]] -===== Example - -[source,curl] --------------------------------------------------- -DELETE /api/apm/settings/agent-configuration -{ - "service" : { - "name": "frontend", - "environment": "production" - } -} --------------------------------------------------- - -//// -******************************************************* -//// - -[[apm-list-config]] -==== List configuration - -[[apm-list-config-req]] -===== Request - -`GET /api/apm/settings/agent-configuration` - -[[apm-list-config-body]] -===== Response body - -[source,js] --------------------------------------------------- -[ - { - "agent_name": "go", - "service": { - "name": "opbeans-go", - "environment": "production" - }, - "settings": { - "transaction_sample_rate": "1", - "capture_body": "off", - "transaction_max_spans": "200" - }, - "@timestamp": 1581934104843, - "applied_by_agent": false, - "etag": "1e58c178efeebae15c25c539da740d21dee422fc" - }, - { - "agent_name": "go", - "service": { - "name": "opbeans-go" - }, - "settings": { - "transaction_sample_rate": "1", - "capture_body": "off", - "transaction_max_spans": "300" - }, - "@timestamp": 1581934111727, - "applied_by_agent": false, - "etag": "3eed916d3db434d9fb7f039daa681c7a04539a64" - }, - { - "agent_name": "nodejs", - "service": { - "name": "frontend" - }, - "settings": { - "transaction_sample_rate": "1", - }, - "@timestamp": 1582031336265, - "applied_by_agent": false, - "etag": "5080ed25785b7b19f32713681e79f46996801a5b" - } -] --------------------------------------------------- - -[[apm-list-config-example]] -===== Example - -[source,curl] --------------------------------------------------- -GET /api/apm/settings/agent-configuration --------------------------------------------------- - -//// -******************************************************* -//// - -[[apm-search-config]] -==== Search configuration - -[[apm-search-config-req]] -===== Request - -`POST /api/apm/settings/agent-configuration/search` - -[role="child_attributes"] -[[apm-search-config-req-body]] -===== Request body - -`service`:: -(required, object) Service identifying the configuration. -+ -.Properties of `service` -[%collapsible%open] -====== -`name` ::: - (required, string) Name of service - -`environment` ::: - (optional, string) Environment of service -====== - -`etag`:: -(required) etag is sent by the APM agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`. - -[[apm-search-config-body]] -===== Response body - -[source,js] --------------------------------------------------- -{ - "_index": ".apm-agent-configuration", - "_id": "CIaqXXABmQCdPphWj8EJ", - "_score": 2, - "_source": { - "agent_name": "nodejs", - "service": { - "name": "frontend" - }, - "settings": { - "transaction_sample_rate": "1", - }, - "@timestamp": 1582031336265, - "applied_by_agent": false, - "etag": "5080ed25785b7b19f32713681e79f46996801a5b" - } -} --------------------------------------------------- - -[[apm-search-config-example]] -===== Example - -[source,curl] --------------------------------------------------- -POST /api/apm/settings/agent-configuration/search -{ - "etag": "1e58c178efeebae15c25c539da740d21dee422fc", - "service" : { - "name": "frontend", - "environment": "production" - } -} --------------------------------------------------- - -//// -******************************************************* -******************************************************* -//// - -[role="xpack"] -[[apm-annotation-api]] -=== Annotation API - -The Annotation API allows you to annotate visualizations in the APM app with significant events, like deployments, -allowing you to easily see how these events are impacting the performance of your existing applications. - -By default, annotations are stored in a newly created `observability-annotations` index. -The name of this index can be changed in your `config.yml` by editing `xpack.observability.annotations.index`. -If you change the default index name, you'll also need to <> accordingly. - -The following APIs are available: - -* <> to create an annotation for APM. -// * <> POST /api/observability/annotation -// * <> GET /api/observability/annotation/:id -// * <> DELETE /api/observability/annotation/:id - -[float] -[[use-annotation-api]] -==== How to use APM APIs - -.Expand for required headers, privileges, and usage details -[%collapsible%closed] -====== -include::api.asciidoc[tag=using-the-APIs] -====== - -//// -******************************************************* -//// - -[[apm-annotation-create]] -==== Create or update annotation - -[[apm-annotation-config-req]] -===== Request - -`POST /api/apm/services/:serviceName/annotation` - -[role="child_attributes"] -[[apm-annotation-config-req-body]] -===== Request body - -`service`:: -(required, object) Service identifying the configuration to create or update. -+ -.Properties of `service` -[%collapsible%open] -====== -`version` ::: - (required, string) Version of service. - -`environment` ::: - (optional, string) Environment of service. -====== - -`@timestamp`:: -(required, string) The date and time of the annotation. Must be in https://www.w3.org/TR/NOTE-datetime[ISO 8601] format. - -`message`:: -(optional, string) The message displayed in the annotation. Defaults to `service.version`. - -`tags`:: -(optional, array) Tags are used by the APM app to distinguish APM annotations from other annotations. -Tags may have additional functionality in future releases. Defaults to `[apm]`. -While you can add additional tags, you cannot remove the `apm` tag. - -[[apm-annotation-config-example]] -===== Example - -The following example creates an annotation for a service named `opbeans-java`. - -[source,curl] --------------------------------------------------- -curl -X POST \ - http://localhost:5601/api/apm/services/opbeans-java/annotation \ --H 'Content-Type: application/json' \ --H 'kbn-xsrf: true' \ --H 'Authorization: Basic YhUlubWZhM0FDbnlQeE6WRtaW49FQmSGZ4RUWXdX' \ --d '{ - "@timestamp": "2020-05-08T10:31:30.452Z", - "service": { - "version": "1.2" - }, - "message": "Deployment 1.2" - }' --------------------------------------------------- - -[[apm-annotation-config-body]] -===== Response body - -[source,js] --------------------------------------------------- -{ - "_index": "observability-annotations", - "_id": "Lc9I93EBh6DbmkeV7nFX", - "_version": 1, - "_seq_no": 12, - "_primary_term": 1, - "found": true, - "_source": { - "message": "Deployment 1.2", - "@timestamp": "2020-05-08T10:31:30.452Z", - "service": { - "version": "1.2", - "name": "opbeans-java" - }, - "tags": [ - "apm", - "elastic.co", - "customer" - ], - "annotation": { - "type": "deployment" - }, - "event": { - "created": "2020-05-09T02:34:43.937Z" - } - } -} --------------------------------------------------- - -//// -******************************************************* -******************************************************* -//// - -[role="xpack"] -[[rum-sourcemap-api]] -=== RUM source map API - -IMPORTANT: This endpoint is only compatible with the -{apm-guide-ref}/index.html[APM integration for Elastic Agent]. - -A source map allows minified files to be mapped back to original source code -- -allowing you to maintain the speed advantage of minified code, -without losing the ability to quickly and easily debug your application. - -For best results, uploading source maps should become a part of your deployment procedure, -and not something you only do when you see unhelpful errors. -That’s because uploading source maps after errors happen won’t make old errors magically readable -- -errors must occur again for source mapping to occur. - -The following APIs are available: - -* <> -* <> -* <> - -[float] -[[limit-sourcemap-api]] -==== Max payload size - -{kib}'s maximum payload size is 1mb. -If you attempt to upload a source map that exceeds the max payload size, you will get a `413` error. - -Before uploading source maps that exceed this default, change the maximum payload size allowed by {kib} -with the <> variable. - -[float] -[[use-sourcemap-api]] -==== How to use APM APIs - -.Expand for required headers, privileges, and usage details -[%collapsible%closed] -====== -include::api.asciidoc[tag=using-the-APIs] -====== - -//// -******************************************************* -//// - -[[rum-sourcemap-post]] -==== Create or update source map - -Create or update a source map for a specific service and version. - -[[rum-sourcemap-post-privs]] -===== Privileges - -The user accessing this endpoint requires `All` Kibana privileges for the {beat_kib_app} feature. -For more information, see <>. - -[[apm-sourcemap-post-req]] -===== Request - -`POST /api/apm/sourcemaps` - -[role="child_attributes"] -[[apm-sourcemap-post-req-body]] -===== Request body - -`service_name`:: -(required, string) The name of the service that the service map should apply to. - -`service_version`:: -(required, string) The version of the service that the service map should apply to. - -`bundle_filepath`:: -(required, string) The absolute path of the final bundle as used in the web application. - -`sourcemap`:: -(required, string or file upload) The source map. It must follow the -https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k[source map revision 3 proposal]. - -[[apm-sourcemap-post-example]] -===== Examples - -The following example uploads a source map for a service named `foo` and a service version of `1.0.0`: - -[source,curl] --------------------------------------------------- -curl -X POST "http://localhost:5601/api/apm/sourcemaps" \ --H 'Content-Type: multipart/form-data' \ --H 'kbn-xsrf: true' \ --H 'Authorization: ApiKey ${YOUR_API_KEY}' \ --F 'service_name="foo"' \ --F 'service_version="1.0.0"' \ --F 'bundle_filepath="/test/e2e/general-usecase/bundle.js"' \ --F 'sourcemap="{\"version\":3,\"file\":\"static/js/main.chunk.js\",\"sources\":[\"fleet-source-map-client/src/index.css\",\"fleet-source-map-client/src/App.js\",\"webpack:///./src/index.css?bb0a\",\"fleet-source-map-client/src/index.js\",\"fleet-source-map-client/src/reportWebVitals.js\"],\"sourcesContent\":[\"content\"],\"mappings\":\"mapping\",\"sourceRoot\":\"\"}"' <1> --------------------------------------------------- -<1> Alternatively, upload the source map as a file with `-F 'sourcemap=@path/to/source_map/bundle.js.map'` - -[[apm-sourcemap-post-body]] -===== Response body - -[source,js] --------------------------------------------------- -{ - "type": "sourcemap", - "identifier": "foo-1.0.0", - "relative_url": "/api/fleet/artifacts/foo-1.0.0/644fd5a997d1ddd90ee131ba18e2b3d03931d89dd1fe4599143c0b3264b3e456", - "body": "eJyFkL1OwzAUhd/Fc+MbYMuCEBIbHRjKgBgc96R16tiWr1OQqr47NwqJxEK3q/PzWccXxchnZ7E1A1SjuhjVZtF2yOxiEPlO17oWox3D3uPFeSRTjmJQARfCPeiAgGx8NTKsYdAc1T3rwaSJGcds8Sp3c1HnhfywUZ3QhMTFFGepZxqMC9oex3CS9tpk1XyozgOlmoVKuJX1DqEQZ0su7PGtLU+V/3JPKc3cL7TJ2FNDRPov4bFta3MDM4f7W69lpJjLO9qdK8bzVPhcJz3HUCQ4LbO/p5hCSC4cZPByrp/wFqOklbpefwAhzpqI", - "created": "2021-07-09T20:47:44.812Z", - "id": "apm:foo-1.0.0-644fd5a997d1ddd90ee131ba18e2b3d03931d89dd1fe4599143c0b3264b3e456", - "compressionAlgorithm": "zlib", - "decodedSha256": "644fd5a997d1ddd90ee131ba18e2b3d03931d89dd1fe4599143c0b3264b3e456", - "decodedSize": 441, - "encodedSha256": "024c72749c3e3dd411b103f7040ae62633558608f480bce4b108cf5b2275bd24", - "encodedSize": 237, - "encryptionAlgorithm": "none", - "packageName": "apm" -} --------------------------------------------------- - -//// -******************************************************* -//// - -[[rum-sourcemap-get]] -==== Get source maps - -Returns an array of Fleet artifacts, including source map uploads. - -[[rum-sourcemap-get-privs]] -===== Privileges - -The user accessing this endpoint requires `Read` or `All` Kibana privileges for the {beat_kib_app} feature. -For more information, see <>. - -[[apm-sourcemap-get-req]] -===== Request - -`GET /api/apm/sourcemaps` - -[[apm-sourcemap-get-example]] -===== Example - -The following example requests all uploaded source maps: - -[source,curl] --------------------------------------------------- -curl -X GET "http://localhost:5601/api/apm/sourcemaps" \ --H 'Content-Type: application/json' \ --H 'kbn-xsrf: true' \ --H 'Authorization: ApiKey ${YOUR_API_KEY}' --------------------------------------------------- - -[[apm-sourcemap-get-body]] -===== Response body - -[source,js] --------------------------------------------------- -{ - "artifacts": [ - { - "type": "sourcemap", - "identifier": "foo-1.0.0", - "relative_url": "/api/fleet/artifacts/foo-1.0.0/644fd5a997d1ddd90ee131ba18e2b3d03931d89dd1fe4599143c0b3264b3e456", - "body": { - "serviceName": "foo", - "serviceVersion": "1.0.0", - "bundleFilepath": "/test/e2e/general-usecase/bundle.js", - "sourceMap": { - "version": 3, - "file": "static/js/main.chunk.js", - "sources": [ - "fleet-source-map-client/src/index.css", - "fleet-source-map-client/src/App.js", - "webpack:///./src/index.css?bb0a", - "fleet-source-map-client/src/index.js", - "fleet-source-map-client/src/reportWebVitals.js" - ], - "sourcesContent": [ - "content" - ], - "mappings": "mapping", - "sourceRoot": "" - } - }, - "created": "2021-07-09T20:47:44.812Z", - "id": "apm:foo-1.0.0-644fd5a997d1ddd90ee131ba18e2b3d03931d89dd1fe4599143c0b3264b3e456", - "compressionAlgorithm": "zlib", - "decodedSha256": "644fd5a997d1ddd90ee131ba18e2b3d03931d89dd1fe4599143c0b3264b3e456", - "decodedSize": 441, - "encodedSha256": "024c72749c3e3dd411b103f7040ae62633558608f480bce4b108cf5b2275bd24", - "encodedSize": 237, - "encryptionAlgorithm": "none", - "packageName": "apm" - } - ] -} --------------------------------------------------- - -//// -******************************************************* -//// - -[[rum-sourcemap-delete]] -==== Delete source map - -Delete a previously uploaded source map. - -[[rum-sourcemap-delete-privs]] -===== Privileges - -The user accessing this endpoint requires `All` Kibana privileges for the {beat_kib_app} feature. -For more information, see <>. - -[[apm-sourcemap-delete-req]] -===== Request - -`DELETE /api/apm/sourcemaps/:id` - -[[apm-sourcemap-delete-example]] -===== Example - -The following example deletes a source map with an id of `apm:foo-1.0.0-644fd5a9`: - -[source,curl] --------------------------------------------------- -curl -X DELETE "http://localhost:5601/api/apm/sourcemaps/apm:foo-1.0.0-644fd5a9" \ --H 'Content-Type: application/json' \ --H 'kbn-xsrf: true' \ --H 'Authorization: ApiKey ${YOUR_API_KEY}' --------------------------------------------------- - -[[apm-sourcemap-delete-body]] -===== Response body - -[source,js] --------------------------------------------------- -{} --------------------------------------------------- - -//// -******************************************************* -******************************************************* -//// - -[role="xpack"] -[[agent-key-api]] -=== APM agent Key API - -The APM agent Key API allows you to configure APM agent keys to authorize requests from APM agents to the APM Server. - -The following APM agent key APIs are available: - -* <> to create an APM agent key - -[float] -[[use-agent-key-api]] -==== How to use APM APIs - -.Expand for required headers, privileges, and usage details -[%collapsible%closed] -====== -include::api.asciidoc[tag=using-the-APIs] -====== - -//// -******************************************************* -//// - -[[apm-create-agent-key]] -==== Create agent key - -Create an APM agent API key. Specify API key privileges in the request body at creation time. - -[[apm-create-agent-key-privileges]] -===== Privileges - -The user creating an APM agent API key must have at least the `manage_own_api_key` cluster privilege -and the APM application-level privileges that it wishes to grant. - -====== Example role - -The example below uses the Kibana <> to create a role named `apm_agent_key_user`. -Create and assign this role to a user that wishes to create APM agent API keys. - -[source,js] --------------------------------------------------- -POST /_security/role/apm_agent_key_user -{ - "cluster": ["manage_own_api_key"], - "applications": [{ - "application": "apm", - "privileges": ["event:write", "config_agent:read"], - "resources": ["*"] - }] -} --------------------------------------------------- - -[[apm-create-agent-key-req]] -===== Request - -`POST /api/apm/agent_keys` - -[role="child_attributes"] -[[apm-create-agent-key-req-body]] -===== Request body - -`name`:: -(required, string) Name of the APM agent key. - -`privileges`:: -(required, array) APM agent key privileges. It can take one or more of the following values: - - - `event:write`. Required for ingesting APM agent events. - - `config_agent:read`. Required for APM agents to read agent configuration remotely. - -[[apm-agent-key-create-example]] -===== Example - -[source,curl] --------------------------------------------------- -POST /api/apm/agent_keys -{ - "name": "apm-key", - "privileges": ["event:write", "config_agent:read"] -} --------------------------------------------------- - -[[apm-agent-key-create-body]] -===== Response body - -[source,js] --------------------------------------------------- -{ - "agentKey": { - "id": "3DCLmn0B3ZMhLUa7WBG9", - "name": "apm-key", - "api_key": "PjGloCGOTzaZr8ilUPvkjA", - "encoded": "M0RDTG1uMEIzWk1oTFVhN1dCRzk6UGpHbG9DR09UemFacjhpbFVQdmtqQQ==" - } -} --------------------------------------------------- - -Once created, you can copy the API key (Base64 encoded) and use it to to authorize requests from APM agents to the APM Server. \ No newline at end of file diff --git a/docs/apm/apm-alerts.asciidoc b/docs/apm/apm-alerts.asciidoc deleted file mode 100644 index 59cfbc50f38dc..0000000000000 --- a/docs/apm/apm-alerts.asciidoc +++ /dev/null @@ -1,175 +0,0 @@ -[role="xpack"] -[[apm-alerts]] -=== Alerts and rules - -++++ -Create an alert -++++ - -The APM app allows you to define **rules** to detect complex conditions within your APM data -and trigger built-in **actions** when those conditions are met. - -The following **rules** are supported: - -* **Threshold rule**: -Alert when the latency or failed transaction rate is abnormal. -Threshold rules can be as broad or as granular as you'd like, enabling you to define exactly when you want to be alerted--whether that's at the environment level, service name level, transaction type level, and/or transaction name level. -* **Anomaly rule**: -Alert when either the latency of a service is anomalous. Anomaly rules can be set at the environment level, service level, and/or transaction type level. -* **Error count rule**: -Alert when the number of errors in a service exceeds a defined threshold. Error count rules can be set at the environment level, service level, and error group level. - -[role="screenshot"] -image::apm/images/apm-alert.png[Create an alert in the APM app] - -Below, we'll walk through the creation of two APM rules. - -For a complete walkthrough of the **Create rule** flyout panel, including detailed information on each configurable property, -see Kibana's <>. - -[float] -[[apm-create-transaction-alert]] -=== Example: create a latency anomaly rule - -Latency anomaly rules trigger when the latency of a service is abnormal. -Because some parts of an application are more important than others, and have a different -tolerance for latency, we'll target a specific transaction within a service. - -Before continuing, identify the service name, transaction type, and environment that you'd like to create a latency anomaly rule for. -This guide will create an alert for all services based on the following criteria: - -* Service: `{your_service.name}` -* Transaction: `{your_transaction.name}` -* Environment: `{your_service.environment}` -* Severity level: critical -* Check every five minutes -* Send an alert to a Slack channel when the rule status changes - -From any page in the APM app, select **Alerts and rules** > **Create anomaly rule**. -Change the name of the rule, but do not edit the tags. - -Based on the criteria above, define the following rule details: - -* **Service** - `{your_service.name}` -* **Type** - `{your_transaction.name}` -* **Environment** - `{your_service.environment}` -* **Has anomaly with severity** - `critical` -* **Check every** - `5 minutes` - -Next, add a connector type. Multiple connectors can be selected, but in this example we're interested in Slack. -Select **Slack** > **Create a connector**. -Enter a name for the connector, -and paste your Slack webhook URL. -See Slack's webhook documentation if you need to create one. - -A default message is provided as a starting point for your alert. -You can use the https://mustache.github.io/[Mustache] template syntax, i.e., `{{variable}}` -to pass additional alert values at the time a condition is detected to an action. -A list of available variables can be accessed by selecting the -**add variable** button image:apm/images/add-variable.png[add variable button]. - -Click **Save**. Your rule has been created and is now active! - -[float] -[[apm-create-error-alert]] -=== Example: create an error count threshold alert - -The error count threshold alert triggers when the number of errors in a service exceeds a defined threshold. -Because some errors are more important than others, this guide will focus a specific error group ID. - -Before continuing, identify the service name, environment name, and error group ID that you'd like to create a latency anomaly rule for. -The easiest way to find an error group ID is to select the service that you're interested in and navigating to the **Errors** tab. - -This guide will create an alert for an error group ID based on the following criteria: - -* Service: `{your_service.name}` -* Environment: `{your_service.environment}` -* Error Grouping Key: `{your_error.ID}` -* Error rate is above 25 errors for the last five minutes -* Group alerts by `service.name` and `service.environment` -* Check every 1 minute -* Send the alert via email to the site reliability team - -From any page in the APM app, select **Alerts and rules** > **Create error count rule**. -Change the name of the alert, but do not edit the tags. - -Based on the criteria above, define the following rule details: - -* **Service**: `{your_service.name}` -* **Environment**: `{your_service.environment}` -* **Error Grouping Key**: `{your_error.ID}` -* **Is above** - `25 errors` -* **For the last** - `5 minutes` -* **Group alerts by** - `service.name` `service.environment` -* **Check every** - `1 minute` - -[NOTE] -==== -Alternatively, you can use a KQL filter to limit the scope of the alert: - -. Toggle on *Use KQL Filter*. -. Add a filter, for example to achieve the same effect as the example above: -+ -[source,txt] ------- -service.name:"{your_service.name}" and service.environment:"{your_service.environment}" and error.grouping_key:"{your_error.ID}" ------- - -Using a KQL Filter to limit the scope is available for _Latency threshold_, _Failed transaction rate threshold_, and -_Error count threshold_ rules. -==== - -Select the **Email** connector and click **Create a connector**. -Fill out the required details: sender, host, port, etc., and click **save**. - -A default message is provided as a starting point for your alert. -You can use the https://mustache.github.io/[Mustache] template syntax, i.e., `{{variable}}` -to pass additional alert values at the time a condition is detected to an action. -A list of available variables can be accessed by selecting the -**add variable** button image:apm/images/add-variable.png[add variable button]. - -Click **Save**. The alert has been created and is now active! - -[float] -[[apm-alert-view-active]] -=== View active alerts - -Active alerts are displayed and grouped in multiple ways in the APM app. - -[float] -[[apm-alert-view-group]] -==== View alerts by service group - -If you're using the <> feature, you can view alerts by service group. -From the service group overview page, click the red alert indicator to open the **Alerts** tab with a predefined filter that matches the filter used when creating the service group. - -[role="screenshot"] -image::apm/images/apm-service-group.png[Example view of service group in the APM app in Kibana] - -[float] -[[apm-alert-view-service]] -==== View alerts by service - -Alerts can be viewed within the context of any service. -After selecting a service, go to the **Alerts** tab to view any alerts that are active for the selected service. - -[role="screenshot"] -image::apm/images/active-alert-service.png[View active alerts by service] - -[float] -[[apm-alert-manage]] -=== Manage alerts and rules - -From the APM app, select **Alerts and rules** > **Manage rules** to be taken to -the {kib} *{rules-ui}* page. -From this page, you can disable, mute, and delete APM alerts. - -[float] -[[apm-alert-more-info]] -=== More information - -See {kibana-ref}/alerting-getting-started.html[Alerting] for more information. - -NOTE: If you are using an **on-premise** Elastic Stack deployment with security, -communication between Elasticsearch and Kibana must have TLS configured. -More information is in the alerting {kibana-ref}/alerting-setup.html#alerting-prerequisites[prerequisites]. diff --git a/docs/apm/apm-app-users.asciidoc b/docs/apm/apm-app-users.asciidoc deleted file mode 100644 index 94ba6ff51bb90..0000000000000 --- a/docs/apm/apm-app-users.asciidoc +++ /dev/null @@ -1,346 +0,0 @@ -[role="xpack"] -[[apm-app-users]] -== APM app users and privileges - -:beat_default_index_prefix: apm -:annotation_index: observability-annotations - -++++ -Users and privileges -++++ - -Use role-based access control to grant users access to secured -resources. The roles that you set up depend on your organization's security -requirements and the minimum privileges required to use specific features. - -{es-security-features} provides {ref}/built-in-roles.html[built-in roles] that grant a -subset of the privileges needed by APM users. -When possible, assign users the built-in roles to minimize the affect of future changes on your security strategy. -If no built-in role is available, you can assign users the privileges needed to accomplish a specific task. -In general, there are three types of privileges you'll work with: - -* **Elasticsearch cluster privileges**: Manage the actions a user can perform against your cluster. -* **Elasticsearch index privileges**: Control access to the data in specific indices your cluster. -* **Kibana feature privileges**: Grant users write or read access to features and apps within Kibana. - -Select your use-case to get started: - -* <> -* <> -* <> -* <> -* <> - -//// -*********************************** *********************************** -//// - -[role="xpack"] -[[apm-app-reader]] -=== APM reader user - -++++ -Create an APM reader user -++++ - -APM reader users typically need to view the APM app and dashboards and visualizations that use APM data. -These users might also need to create and edit dashboards, visualizations, and machine learning jobs. - -[[apm-app-reader-full]] -==== APM reader - -To create an APM reader user: - -. Create a new role, named something like `read-apm`, and assign the following privileges: -+ --- -:apm-read-view: -:apm-monitor: -include::./tab-widgets/apm-app-reader/widget.asciidoc[] -:!apm-read-view: -:!apm-monitor: --- - -. Assign the `read-apm` role created in the previous step, and the following built-in roles to -any APM reader users: -+ -[options="header"] -|==== -|Role | Purpose - -|`kibana_admin` -|Grants access to all features in Kibana. - -|`machine_learning_admin` -|Grants the privileges required to create, update, and view machine learning jobs -|==== - -[[apm-app-reader-partial]] -==== Partial APM reader - -In some instances, you may wish to restrict certain Kibana apps that a user has access to. - -. Create a new role, named something like `read-apm-partial`, and assign the following privileges: -+ --- -include::./tab-widgets/apm-app-reader/widget.asciidoc[] --- - -. Assign feature privileges to any Kibana feature that the user needs access to. -Here are two examples: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -| Kibana -| `Read` or `All` on the {beat_kib_app} feature -| Allow the use of the the {beat_kib_app} apps - -| Kibana -| `Read` or `All` on Dashboards and Discover -| Allow the user to view, edit, and create dashboards, as well as browse data. -|==== - -. Finally, assign the following role if a user needs to enable and edit machine learning features: -+ -[options="header"] -|==== -|Role | Purpose - -|`machine_learning_admin` -|Grants the privileges required to create, update, and view machine learning jobs -|==== - -//// -*********************************** *********************************** -//// - -[role="xpack"] -[[apm-app-annotation-user-create]] -=== APM app annotation user - -++++ -Create an annotation user -++++ - -NOTE: By default, the `viewer` and `editor` built-in roles provide read access to Observability annotations. -You only need to create an annotation user to write to the annotations index -(<>). - -[[apm-app-annotation-user]] -==== Annotation user - -View deployment annotations in the APM app. - -. Create a new role, named something like `annotation_user`, -and assign the following privileges: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -|Index -|`read` on +\{ANNOTATION_INDEX\}+^1^ -|Read-only access to the observability annotation index - -|Index -|`view_index_metadata` on +\{ANNOTATION_INDEX\}+^1^ -|Read-only access to observability annotation index metadata -|==== -+ -^1^ +\{ANNOTATION_INDEX\}+ should be the index name you've defined in -<>. - -. Assign the `annotation_user` created previously, and the roles and privileges necessary to create -a <> or <> APM reader to any users that need to view annotations in the APM app - -[[apm-app-annotation-api]] -==== Annotation API - -See <>. - -//// -*********************************** *********************************** -//// - -[role="xpack"] -[[apm-app-central-config-user]] -=== APM app central config user - -++++ -Create a central config user -++++ - -[[apm-app-central-config-manager]] -==== Central configuration manager - -Central configuration users need to be able to view, create, update, and delete APM agent configurations. - -. Create a new role, named something like `central-config-manager`, and assign the following privileges: -+ --- -include::./tab-widgets/central-config-users/widget.asciidoc[] --- -+ -TIP: Using the deprecated APM Server binaries? -Add the privileges under the **Classic APM indices** tab above. - -. Assign the `central-config-manager` role created in the previous step, -and the following Kibana feature privileges to anyone who needs to manage central configurations: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -| Kibana -|`All` on the {beat_kib_app} feature -|Allow full use of the {beat_kib_app} apps -|==== - -[[apm-app-central-config-reader]] -==== Central configuration reader - -In some instances, you may wish to create a user that can only read central configurations, -but not create, update, or delete them. - -. Create a new role, named something like `central-config-reader`, and assign the following privileges: -+ --- -include::./tab-widgets/central-config-users/widget.asciidoc[] --- -+ -TIP: Using the deprecated APM Server binaries? -Add the privileges under the **Classic APM indices** tab above. - -. Assign the `central-config-reader` role created in the previous step, -and the following Kibana feature privileges to anyone who needs to read central configurations: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -| Kibana -|`read` on the {beat_kib_app} feature -|Allow read access to the {beat_kib_app} apps -|==== - -[[apm-app-central-config-api]] -==== Central configuration API - -See <>. - -//// -*********************************** *********************************** -//// - -[role="xpack"] -[[apm-app-storage-explorer-user-create]] -=== APM app storage explorer user - -++++ -Create a storage explorer user -++++ - -[[apm-app-storage-explorer-user]] -==== Storage Explorer user - -View the **Storage Explorer** in the APM app. - -. Create a new role, named something like `storage-explorer_user`, -and assign the following privileges: -+ --- -include::./tab-widgets/storage-explorer-user/widget.asciidoc[] --- - -. Assign the `storage-explorer_user` created previously, and the roles and privileges necessary to create -a <> or <> APM reader to any users that need to view **Storage Explorer** in the APM app. - -//// -*********************************** *********************************** -//// - -[role="xpack"] -[[apm-app-api-user]] -=== APM app API user - -++++ -Create an API user -++++ - -[[apm-app-api-config-manager]] -==== Central configuration API - -Users can list, search, create, update, and delete central configurations via the APM app API. - -. Assign the following Kibana feature privileges: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -| Kibana -|`all` on the {beat_kib_app} feature -|Allow all access to the {beat_kib_app} apps -|==== - -[[apm-app-api-config-reader]] -==== Central configuration API reader - -Sometimes a user only needs to list and search central configurations via the APM app API. - -. Assign the following Kibana feature privileges: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -| Kibana -|`read` on the {beat_kib_app} feature -|Allow read access to the {beat_kib_app} apps -|==== - -[[apm-app-api-annotation-manager]] -==== Annotation API - -Users can use the annotation API to create annotations on their APM data. - -. Create a new role, named something like `annotation_role`, -and assign the following privileges: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -|Index -|`manage` on +{annotation_index}+ index -|Check if the +{annotation_index}+ index exists - -|Index -|`read` on +{annotation_index}+ index -|Read the +{annotation_index}+ index - -|Index -|`create_index` on +{annotation_index}+ index -|Create the +{annotation_index}+ index - -|Index -|`create_doc` on +{annotation_index}+ index -|Create new annotations in the +{annotation_index}+ index -|==== - -. Assign the `annotation_role` created previously, -and the following Kibana feature privileges to any annotation API users: -+ -[options="header"] -|==== -|Type | Privilege | Purpose - -| Kibana -|`all` on the {beat_kib_app} feature -|Allow all access to the {beat_kib_app} apps -|==== - -//LEARN MORE -//Learn more about <>. diff --git a/docs/apm/apm-spaces.asciidoc b/docs/apm/apm-spaces.asciidoc deleted file mode 100644 index 093b7560cd5aa..0000000000000 --- a/docs/apm/apm-spaces.asciidoc +++ /dev/null @@ -1,415 +0,0 @@ -[role="xpack"] -[[apm-spaces]] -=== Control access to APM data - -Starting in version 8.2.0, the APM app is <> aware. -This allows you to separate your data--and access to that data--by team, use case, service environment, -or any other filter that you choose. - -To take advantage of this feature, your APM data needs to be written to different data steams. -One way to accomplish this is with different namespaces. -For example, you can send production data to an APM integration with a namespace of `production`, -while sending staging data to a different APM integration with a namespace of `staging`. - -Multiple APM integration instances is not required though. The simplest way to take advantage of this feature -is by creating filtered aliases. See the guide below for more information. - -[float] -[[apm-spaces-example]] -=== Guide: Separate staging and production data - -This guide will explain how to separate your staging and production data. -This can be helpful to either remove noise when troubleshooting a production issue, -or to create more granular access control for certain data. - -This guide assumes that you: - -* Are sending both staging and production APM data to an {es} cluster. -* Have configured the `environment` variable in your APM agent configurations. -This variable sets the `service.environment` field in APM documents. -You should have documents where `service.environment: production` and `service.environment: staging`. -If this field is empty, see <> to learn how to set this value. - -[float] -==== Step 1: Create filtered aliases - -The APM app uses index patterns to query your APM data. An index pattern can match data streams, indices, and/or aliases. -The default values are: - -[options="header"] -|==== -| Index setting | Default index pattern -| Error | `logs-apm*` -| Span/Transaction | `traces-apm*` -| Metrics | `metrics-apm*` -|==== - -NOTE: The default index settings also query the `apm-*` data view. -This data view matches APM data shipped in earlier versions of APM (prior to v8.0). - -Instead of querying the default APM data views, we can create filtered aliases for the APM app to query. -A filtered alias is a secondary name for a group of data streams that has a user-defined -filter to limit the documents that the alias can access. - -To separate `staging` and `production` APM data, we'd need to create six filtered aliases--three -aliases for each service environment: - -[options="header"] -|==== -| Index setting | `production` env | `staging` env -| Error | `production-logs-apm` | `staging-logs-apm` -| Span/Transaction | `production-traces-apm` | `staging-traces-apm` -| Metrics | `production-metrics-apm` | `staging-metrics-apm` -|==== - -The `production--apm` aliases will contain a filter that only provides access to documents -where the `service.environment` is `production`. -Similarly, the `staging--apm` aliases will contain a filter that only provides access to documents -where the `service.environment` is `staging`. - -To create these six filtered aliases, use the {es} {ref}/indices-aliases.html[Aliases API]. -In {kib}, open **Dev Tools** and run the following POST requests. - -[%collapsible%open] -.`traces-apm*` production alias example -==== -[source, console] ----- -POST /_aliases?pretty -{ - "actions": [ - { - "add": { - "index": "traces-apm*", <1> - "alias": "production-traces-apm", <2> - "filter": { - "term": { - "service.environment": { - "value": "production" <3> - } - } - } - } - } - ] -} ----- -<1> This example matches the APM traces data stream -<2> The alias must not match the default APM index (`traces-apm*,apm-*`) -<3> Only match documents where `service.environment: production` -==== - -[%collapsible] -.`logs-apm*` production alias example -==== -[source, console] ----- -POST /_aliases?pretty -{ - "actions": [ - { - "add": { - "index": "logs-apm*", <1> - "alias": "production-logs-apm", <2> - "filter": { - "term": { - "service.environment": { - "value": "production" <3> - } - } - } - } - } - ] -} ----- -<1> This example matches the APM logs data stream -<2> The alias must not match the default APM index (`logs-apm*,apm-*`) -<3> Only match documents where `service.environment: production` -==== - -[%collapsible] -.`metrics-apm*` production alias example -==== -[source, console] ----- -POST /_aliases?pretty -{ - "actions": [ - { - "add": { - "index": "metrics-apm*", <1> - "alias": "production-metrics-apm", <2> - "filter": { - "term": { - "service.environment": { - "value": "production" <3> - } - } - } - } - } - ] -} ----- -<1> This example matches the APM metrics data stream -<2> The alias must not match the default APM index (`metrics-apm*,apm-*`) -<3> Only match documents where `service.environment: production` -==== - -[%collapsible] -.`traces-apm*` staging alias example -==== -[source, console] ----- -POST /_aliases?pretty -{ - "actions": [ - { - "add": { - "index": "traces-apm*", <1> - "alias": "staging-traces-apm", <2> - "filter": { - "term": { - "service.environment": { - "value": "staging" <3> - } - } - } - } - } - ] -} ----- -<1> This example matches the APM traces data stream -<2> The alias must not match the default APM index (`traces-apm*,apm-*`) -<3> Only match documents where `service.environment: staging` -==== - -[%collapsible] -.`logs-apm*` staging alias example -==== -[source, console] ----- -POST /_aliases?pretty -{ - "actions": [ - { - "add": { - "index": "logs-apm*", <1> - "alias": "staging-logs-apm", <2> - "filter": { - "term": { - "service.environment": { - "value": "staging" <3> - } - } - } - } - } - ] -} ----- -<1> This example matches the APM logs data stream -<2> The alias must not match the default APM index (`logs-apm*,apm-*`) -<3> Only match documents where `service.environment: staging` -==== - -[%collapsible] -.`metrics-apm*` staging alias example -==== -[source, console] ----- -POST /_aliases?pretty -{ - "actions": [ - { - "add": { - "index": "metrics-apm*", <1> - "alias": "staging-metrics-apm", <2> - "filter": { - "term": { - "service.environment": { - "value": "staging" <3> - } - } - } - } - } - ] -} ----- -<1> This example matches the APM metrics data stream -<2> The alias must not match the default APM index (`metrics-apm*,apm-*`) -<3> Only match documents where `service.environment: staging` -==== - -[float] -==== Step 2: Create {kib} spaces - -Next, you'll need to create a {Kib} space for each service environment. -To create these spaces, navigate to **Stack Management** > **Spaces** > **Create a space**. -For this guide, we've created two Kibana spaces, one named `production` and one named `staging`. - -See <> for more information on creating a space. - -[float] -==== Step 3: Update APM index settings in each space - -Now we can change the default data views that the APM app queries in each space. - -Open the APM app and navigate to **Settings** > **Indices**. -Use the table below to update your settings for each space. -The values in each column match the names of the filtered aliases we created in step one. - -[options="header"] -|==== -| Index setting | `production` space | `staging` space -| Error indices | `production-logs-apm` | `staging-logs-apm` -| Span indices | `production-traces-apm` | `staging-traces-apm` -| Transaction indices | `production-traces-apm` | `staging-traces-apm` -| Metrics indices | `production-metrics-apm` | `staging-metrics-apm` -|==== - -[role="screenshot"] -image::settings/images/apm-settings.png[APM app settings in Kibana] - -[float] -==== Step 4: Create {kib} access roles - -In {kib}, navigate to **Stack Management** > **Roles** and click **Create role**. - -You'll need to create two roles: one for `staging` users (we'll call this role `staging_apm_viewer`) -and one for `production` users (we'll call this role `production_apm_viewer`). - -Using the table below, assign each role the following privileges: - -[options="header"] -|==== -| Privileges | `production_apm_viewer` | `staging_apm_viewer` -| Index privileges | index: `production-*-apm`, privilege: `read` | index: `staging-*-apm`, privilege: `read` -| Kibana privileges | space: `production`, feature privileges: `APM and User Experience: read` | space: `staging`, feature privileges: `APM and User Experience: read` -|==== - -[role="screenshot"] -image::./images/apm-roles-config.png[APM role config example] - -Alternatively, you can use the -{es} {ref}/security-api-put-role.html[Create or update roles API]: - -[%collapsible%open] -.Create a `production_apm_viewer` role -==== -This request creates a `production_apm_viewer` role: - -[source, console] ----- -POST /_security/role/production_apm_viewer -{ - "cluster": [ ], - "indices": [ - { - "names": ["production-*-apm"], <1> - "privileges": ["read"] - } - ], - "applications": [ - { - "application" : "kibana-.kibana", - "privileges" : [ - "feature_apm.read" <2> - ], - "resources" : [ - "space:production" <3> - ] - } - ] -} ----- -<1> This data view matches all of the production aliases created in step one. -<2> Assigns `read` privileges for the APM and User Experience apps. -<3> Provides access to the space named `production`. -==== - -[%collapsible] -.Create a `staging_apm_viewer` role -==== -This request creates a `staging_apm_viewer` role: - -[source, console] ----- -POST /_security/role/staging_apm_viewer -{ - "cluster": [ ], - "indices": [ - { - "names": ["staging-*-apm"], <1> - "privileges": ["read"] - } - ], - "applications": [ - { - "application" : "kibana-.kibana", - "privileges" : [ - "feature_apm.read" <2> - ], - "resources" : [ - "space:staging" <3> - ] - } - ] -} ----- -<1> This data view matches all of the staging aliases created in step one. -<2> Assigns `read` privileges for the APM and User Experience apps. -<3> Provides access to the space named `staging`. -==== - -[float] -==== Step 5: Assign users to roles - -The last thing to do is assign users to the newly created roles above. -Users will only have access to the data within the spaces that they are granted. - -For information on how to create users and assign them roles with the {kib} UI, -see <>. - -Alternatively, you can use the -{es} {ref}/security-api-put-user.html[Create or update users API]. - -This example creates a new user and assigns them the `production_apm_viewer` role created in the previous step. -This user will only have access to the production space and data with a `service.environment` of `production`. -Remember to change the `password`, `full_name`, and `email` fields. - -[source, console] ----- -POST /_security/user/production-apm-user -{ - "password" : "l0ng-r4nd0m-p@ssw0rd", - "roles" : [ "production_apm_viewer" ], <1> - "full_name" : "Jane Production Smith", - "email" : "janesmith@example.com" -} ----- -<1> Assigns the previously created `production_apm_viewer` role. - -This example creates a new user and assigns them the `staging_apm_viewer` role created in the previous step. -This user will only have access to the staging space and data with a `service.environment` of `staging`. -Remember to change the `password`, `full_name`, and `email` fields. - -[source, console] ----- -POST /_security/user/staging-apm-user -{ - "password" : "l0ng-r4nd0m-p@ssw0rd", - "roles" : [ "staging_apm_viewer" ], <1> - "full_name" : "John Staging Doe", - "email" : "johndoe@example.com" -} ----- -<1> Assigns the previously created `staging_apm_viewer` role. - -[float] -==== Step 6: Marvel - -That's it! Head back to the APM app and marvel at your space-specific data. diff --git a/docs/apm/correlations.asciidoc b/docs/apm/correlations.asciidoc deleted file mode 100644 index ca77c6c8c6afb..0000000000000 --- a/docs/apm/correlations.asciidoc +++ /dev/null @@ -1,89 +0,0 @@ -[role="xpack"] -[[correlations]] -=== Find transaction latency and failure correlations - -Correlations surface attributes of your data that are potentially correlated -with high-latency or erroneous transactions. For example, if you are a site -reliability engineer who is responsible for keeping production systems up and -running, you want to understand what is causing slow transactions. Identifying -attributes that are responsible for higher latency transactions can potentially -point you toward the root cause. You may find a correlation with a particular -piece of hardware, like a host or pod. Or, perhaps a set of users, based on IP -address or region, is facing increased latency due to local data center issues. - -To find correlations, select a service on the *Services* page in the {apm-app} -then select a transaction group from the *Transactions* tab. - -NOTE: Queries within the {apm-app} are also applied to the correlations. - -[discrete] -[[correlations-latency]] -==== Find high transaction latency correlations - -The correlations on the *Latency correlations* tab help you discover which -attributes are contributing to increased transaction latency. - -[role="screenshot"] -image::apm/images/correlations-hover.png[Latency correlations] - -The progress bar indicates the status of the asynchronous analysis, which -performs statistical searches across a large number of attributes. For large -time ranges and services with high transaction throughput, this might take some -time. To improve performance, reduce the time range. - -The latency distribution chart visualizes the overall latency of the -transactions in the transaction group. If there are attributes that have a -statistically significant correlation with slow response times, they are listed -in a table below the chart. The table is sorted by correlation coefficients that -range from 0 to 1. Attributes with higher correlation values are more likely to -contribute to high latency transactions. By default, the attribute with the -highest correlation value is added to the chart. To see the latency distribution -for other attributes, select their row in the table. - -If a correlated attribute seems noteworthy, use the **Filter** quick links: - -* `+` creates a new query in the {apm-app} for filtering transactions containing -the selected value. -* `-` creates a new query in the {apm-app} to filter out transactions containing -the selected value. - -You can also click the icon beside the field name to view and filter its most -popular values. - -In this example screenshot, there are transactions that are skewed to the right -with slower response times than the overall latency distribution. If you select -the `+` filter in the appropriate row of the table, it creates a new query in -the {apm-app} for transactions with this attribute. With the "noise" now -filtered out, you can begin viewing sample traces to continue your investigation. - -[discrete] -[[correlations-error-rate]] -==== Find failed transaction correlations - -The correlations on the *Failed transaction correlations* tab help you discover -which attributes are most influential in distinguishing between transaction -failures and successes. In this context, the success or failure of a transaction -is determined by its {ecs-ref}/ecs-event.html#field-event-outcome[event.outcome] -value. For example, APM agents set the `event.outcome` to `failure` when an HTTP -transaction returns a `5xx` status code. - -The chart highlights the failed transactions in the overall latency distribution -for the transaction group. If there are attributes that have a statistically -significant correlation with failed transactions, they are listed in a table. -The table is sorted by scores, which are mapped to high, medium, or low impact -levels. Attributes with high impact levels are more likely to contribute to -failed transactions. By default, the attribute with the highest score is added -to the chart. To see a different attribute in the chart, select its row in the -table. - -For example, in the screenshot below, there are attributes such as a specific -node and pod name that have medium impact on the failed transactions. - -[role="screenshot"] -image::apm/images/correlations-failed-transactions.png[Failed transaction correlations] - -Select the `+` filter to create a new query in the {apm-app} for transactions -with one or more of these attributes. If you are unfamiliar with a field, click -the icon beside its name to view its most popular values and optionally filter -on those values too. Each time that you add another attribute, it is filtering -out more and more noise and bringing you closer to a diagnosis. diff --git a/docs/apm/custom-links.asciidoc b/docs/apm/custom-links.asciidoc deleted file mode 100644 index 4fdf39b643f94..0000000000000 --- a/docs/apm/custom-links.asciidoc +++ /dev/null @@ -1,222 +0,0 @@ -[role="xpack"] -[[custom-links]] -=== Custom links - -++++ -Create custom links -++++ - -Elastic's custom link feature allows you to easily create up to 500 dynamic links -based on your specific APM data. -Custom links can be filtered to only appear in the APM app for relevant services, -environments, transaction types, or transaction names. - -Ready to dive in? Jump straight to the <>. - -[float] -[[custom-links-create]] -=== Create a link - -Each custom link consists of a label, URL, and optional filter. -The easiest way to create a custom link is from within the actions dropdown in the transaction detail page. -This method will automatically apply filters, scoping the link to that specific service, -environment, transaction type, and transaction name. - -Alternatively, you can create a custom link in the APM app by navigating to **Settings** > **Customize UI**, -and selecting **Create custom link**. - -[float] -[[custom-links-label]] -==== Label - -The name of your custom link. -The actions context menu displays this text, so keep it as short as possible. - -TIP: Custom links are displayed alphabetically in the actions menu. - -[float] -[[custom-links-url]] -==== URL - -The URL your link points to. -URLs support dynamic field name variables, encapsulated in double curly brackets: `{{field.name}}`. -These variables will be replaced with transaction metadata when the link is clicked. - -Because everyone's data is different, -you'll need to examine your traces to see what metadata is available for use. -To do this, select a trace in the APM app, and click **Metadata** in the **Trace Sample** table. - -[role="screenshot"] -image::apm/images/example-metadata.png[Example metadata] - -[float] -[[custom-links-filters]] -==== Filters - -Filter each link to only appear for specific services or transactions. -You can filter on the following fields: - -* `service.name` -* `service.env` -* `transaction.type` -* `transaction.name` - -Multiple values are allowed when comma-separated. - -[float] -[[custom-links-examples]] -=== Custom link examples - -// Relevant documentation links -:jira-query-params: https://confluence.atlassian.com/jirakb/how-to-create-issues-using-direct-html-links-in-jira-server-159474.html -:github-query-params: https://help.github.com/en/github/managing-your-work-on-github/about-automation-for-issues-and-pull-requests-with-query-parameters - -Not sure where to start with custom links? -Take a look at the examples below and customize them to your liking! - -[float] -[[custom-links-examples-email]] -==== Email - -Email the owner of a service. - -|==== -|Label |`Email engineer` -|Link |`mailto:@.com` -|Filters |`service.name:` -|==== - -**Example** - -This link opens an email addressed to the team or owner of `python-backend`. -It will only appear on services with the name `python-backend`. - -|==== -|Label |`Email python-backend engineers` -|Link |`mailto:python_team@elastic.co` -|Filters |`service.name:python-backend` -|==== - -[float] -[[custom-links-examples-gh]] -==== GitHub issue - -Open a GitHub issue with pre-populated metadata from the selected trace sample. - -|==== -|Label |`Open an issue in ` -|Link |`https://github.com///issues/new?title=&body=<BODY>` -|Filters |`service.name:client` -|==== - -**Example** - -This link opens a new GitHub issue in the apm-agent-rum repository. -It populates the issue body with relevant metadata from the currently active trace. -Clicking this link results in the following issue being created: - -[role="screenshot"] -image::apm/images/create-github-issue.png[Example github issue] - -|==== -|Label |`Open an issue in apm-rum-js` -|Link |`https://github.com/elastic/apm-agent-rum-js/issues/new?title=Investigate+APM+trace&body=Investigate+the+following+APM+trace%3A%0D%0A%0D%0Aservice.name%3A+{{service.name}}%0D%0Atransaction.id%3A+{{transaction.id}}%0D%0Acontainer.id%3A+{{container.id}}%0D%0Aurl.full%3A+{{url.full}}` -|Filters |`service.name:client` -|==== - -See the {github-query-params}[GitHub automation documentation] for a full list of supported query parameters. - -[float] -[[custom-links-examples-jira]] -==== Jira task - -Create a Jira task with pre-populated metadata from the selected trace sample. - -|==== -|Label |`Open an issue in Jira` -|Link |`https://<JIRA_BASE_URL>/secure/CreateIssueDetails!init.jspa?<ARGUMENTS>` -|==== - -**Example** - -This link creates a new task on the Engineering board in Jira. -It populates the issue body with relevant metadata from the currently active trace. -Clicking this link results in the following task being created in Jira: - -[role="screenshot"] -image::apm/images/create-jira-issue.png[Example jira issue] - -|==== -|Label |`Open a task in Jira` -|Link |`https://test-site-33.atlassian.net/secure/CreateIssueDetails!init.jspa?pid=10000&issuetype=10001&summary=Created+via+APM&description=Investigate+the+following+APM+trace%3A%0D%0A%0D%0Aservice.name%3A+{{service.name}}%0D%0Atransaction.id%3A+{{transaction.id}}%0D%0Acontainer.id%3A+{{container.id}}%0D%0Aurl.full%3A+{{url.full}}` -|==== - -See the {jira-query-params}[Jira application administration knowledge base] -for a full list of supported query parameters. - -[float] -[[custom-links-examples-kib]] -==== Kibana dashboards - -Link to a custom dashboard in Kibana. - -|==== -|Label |`Open transaction in custom visualization` -|Link |`https://kibana-instance/app/kibana#/dashboard?_g=query:(language:kuery,query:'transaction.id:{{transaction.id}}'...` -|==== - -**Example** - -This link opens the current `transaction.id` in a custom kibana dashboard. -There are no filters set. - -|==== -|Label |`Open transaction in Python drilldown viz` -|URL |`https://kibana-instance/app/kibana#/dashboard?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-24h,to:now))&_a=(description:'',filters:!(),fullScreenMode:!f,options:(hidePanelTitles:!f,useMargins:!t),panels:!((embeddableConfig:(),gridData:(h:15,i:cb79c1c0-1af8-472c-aaf7-d158a76946fb,w:24,x:0,y:0),id:c8c74b20-6a30-11ea-92ab-b5d3feff11df,panelIndex:cb79c1c0-1af8-472c-aaf7-d158a76946fb,type:visualization,version:'7.7')),query:(language:kuery,query:'transaction.id:{{transaction.id}}'),timeRestore:!f,title:'',viewMode:edit)` -|==== - -[float] -[[custom-links-examples-slack]] -==== Slack channel - -Open a specified slack channel. - -|==== -|Label |`Open SLACK_CHANNEL` -|Link |`https://COMPANY_SLACK.slack.com/archives/SLACK_CHANNEL` -|Filters |`service.name` : `SERVICE_NAME` -|==== - -**Example** - -This link opens a company slack channel, #apm-support. -It only appears when `transaction.name` is `GET user/login`. - -|==== -|Label |`Open #apm-user-support` -|Link |`https://microsoft.slack.com/archives/efk52kt23k` -|Filters |`transaction.name:GET user/login` -|==== - -[float] -[[custom-links-examples-web]] -==== Website - -Open an internal or external website. - -|==== -|Label |`Open <WEBSITE>` -|Link |`https://<COMPANY_SLACK>.slack.com/archives/<SLACK_CHANNEL>` -|Filters |`service.name:<SERVICE_NAME>` -|==== - -**Example** - -This link opens more data on a specific `user.email`. -It only appears on front-end transactions. - -|==== -|Label |`View user internally` -|Link |`https://internal-site.company.com/user/{{user.email}}` -|Filters |`service.name:client` -|==== diff --git a/docs/apm/dependencies.asciidoc b/docs/apm/dependencies.asciidoc deleted file mode 100644 index 4ac5cdef3f71c..0000000000000 --- a/docs/apm/dependencies.asciidoc +++ /dev/null @@ -1,48 +0,0 @@ -[role="xpack"] -[[dependencies]] -=== Dependencies - -APM agents collect details about external calls made from instrumented services. -Sometimes, these external calls resolve into a downstream service that's instrumented -- in these cases, -you can utilize <<distributed-tracing,distributed tracing>> to drill down into problematic downstream services. -Other times, though, it's not possible to instrument a downstream dependency -- -like with a database or third-party service. -**Dependencies** gives you a window into these uninstrumented, downstream dependencies. - -[role="screenshot"] -image::apm/images/dependencies.png[Dependencies view in the APM app in Kibana] - -Many application issues are caused by slow or unresponsive downstream dependencies. -And because a single, slow dependency can significantly impact the end-user experience, -it's important to be able to quickly identify these problems and determine the root cause. - -Select a dependency to see detailed latency, throughput, and failed transaction rate metrics. - -[role="screenshot"] -image::apm/images/dependencies-drilldown.png[Dependencies drilldown view in the APM app in Kibana] - -When viewing a dependency, consider your pattern of usage with that dependency. -If your usage pattern _hasn't_ increased or decreased, -but the experience has been negatively effected -- either with an increase in latency or errors, -there's likely a problem with the dependency that needs to be addressed. - -If your usage pattern _has_ changed, the dependency view can quickly show you whether -that pattern change exists in all upstream services, or just a subset of your services. -You might then start digging into traces coming from -impacted services to determine why that pattern change has occurred. - -[float] -[[dependencies-operations]] -==== Operations - -beta::[] - -**Dependency operations** provides a granular breakdown of the operations/queries a dependency is executing. - -[role="screenshot"] -image::apm/images/operations.png[operations view in the APM app in Kibana] - -Selecting an operation displays the operation's impact and performance trends over time, via key metrics like latency, throughput, and failed transaction rate. In addition, the <<spans,**Trace sample timeline**>> provides a visual drill-down into an end-to-end trace sample. - -[role="screenshot"] -image::apm/images/operations-detail.png[operations detail view in the APM app in Kibana] diff --git a/docs/apm/deployment-annotations.asciidoc b/docs/apm/deployment-annotations.asciidoc deleted file mode 100644 index 8add9c58a4cab..0000000000000 --- a/docs/apm/deployment-annotations.asciidoc +++ /dev/null @@ -1,48 +0,0 @@ -[role="xpack"] -[[transactions-annotations]] -=== Track deployments with annotations - -++++ -<titleabbrev>Track deployments with annotations</titleabbrev> -++++ - -[role="screenshot"] -image::apm/images/apm-transaction-annotation.png[Example view of transactions annotation in the APM app in Kibana] - -For enhanced visibility into your deployments, we offer deployment annotations on all transaction charts. -This feature enables you to easily determine if your deployment has increased response times for an end-user, -or if the memory/CPU footprint of your application has changed. -Being able to quickly identify bad deployments enables you to rollback and fix issues without causing costly outages. - -By default, automatic deployment annotations are enabled. -This means the APM app will create an annotation on your data when the `service.version` of your application changes. - -Alternatively, you can explicitly create deployment annotations with our annotation API. -The API can integrate into your CI/CD pipeline, -so that each time you deploy, a POST request is sent to the annotation API endpoint: - -[source,curl] ----- -curl -X POST \ - http://localhost:5601/api/apm/services/${SERVICE_NAME}/annotation \ <1> --H 'Content-Type: application/json' \ --H 'kbn-xsrf: true' \ --H 'Authorization: Basic ${API_KEY}' \ <2> --d '{ - "@timestamp": "${DEPLOY_TIME}", <3> - "service": { - "version": "${SERVICE_VERSION}" <4> - }, - "message": "${MESSAGE}" <5> - }' ----- -<1> The `service.name` of your application -<2> An APM app API key with sufficient privileges -<3> The time of the deployment -<4> The `service.version` to be displayed in the annotation -<5> A custom message to be displayed in the annotation - -See the <<apm-annotation-api,annotation API>> reference for more information. - - -NOTE: If custom annotations have been created for the selected time period, any derived annotations, i.e., those created automatically when `service.version` changes, will not be shown. diff --git a/docs/apm/errors.asciidoc b/docs/apm/errors.asciidoc deleted file mode 100644 index 5cc9aebc2e220..0000000000000 --- a/docs/apm/errors.asciidoc +++ /dev/null @@ -1,36 +0,0 @@ -[role="xpack"] -[[errors]] -=== Errors - -TIP: {apm-guide-ref}/data-model-errors.html[Errors] are groups of exceptions with a similar exception or log message. - -The *Errors* overview provides a high-level view of the exceptions that APM agents catch, -or that users manually report with APM agent APIs. -Like errors are grouped together to make it easy to quickly see which errors are affecting your services, -and to take actions to rectify them. - -A service returning a 5xx code from a request handler, controller, etc., will not create -an exception that an APM agent can catch, and will therefore not show up in this view. - -[role="screenshot"] -image::apm/images/apm-errors-overview.png[APM Errors overview] - -Selecting an error group ID or error message brings you to the *Error group*. - -[role="screenshot"] -image::apm/images/apm-error-group.png[APM Error group] - -The error group details page visualizes the number of error occurrences over time and compared to a recent time range. -This allows you to quickly determine if the error rate is changing or remaining constant. -You'll also see the top 5 affected transactions--enabling you to quickly narrow down which transactions are most impacted -by the selected error. - -Further down, you'll see an Error sample. -The error shown is always the most recent to occur. -The sample includes the exception message, culprit, stack trace where the error occurred, -and additional contextual information to help debug the issue--all of which can be copied with the click of a button. - -In some cases, you might also see a Transaction sample ID. -This feature allows you to make a connection between the errors and transactions, -by linking you to the specific transaction where the error occurred. -This allows you to see the whole trace, including which services the request went through. diff --git a/docs/apm/filters.asciidoc b/docs/apm/filters.asciidoc deleted file mode 100644 index e3d085b771a85..0000000000000 --- a/docs/apm/filters.asciidoc +++ /dev/null @@ -1,46 +0,0 @@ -[role="xpack"] -[[filters]] -=== Filters - -++++ -<titleabbrev>Filter data</titleabbrev> -++++ - -Global filters are ways you can filter data across the APM app based on a specific -time range or environment. When viewing a specific service, the filter persists -as you move between tabs. - -[role="screenshot"] -image::apm/images/global-filters.png[Global filters available in the APM app in Kibana] - -[NOTE] -===== -If you prefer to use advanced queries on your data to filter on specific pieces -of information, see <<advanced-queries,Query your data>>. -===== - -[[global-time-range]] -==== Global time range - -The <<set-time-filter,global time range filter>> in {kib} restricts APM data to a specific time period. - -[[environment-selector]] -==== Service environment filter - -The environment selector is a global filter for `service.environment`. -It allows you to view only relevant data and is especially useful for separating development from production environments. -By default, all environments are displayed. If there are no environment options, you'll see "not defined". - -Service environments are defined when configuring your APM agents. -It's vital to be consistent when naming environments in your APM agents. -To learn how to configure service environments, see the specific APM agent documentation: - -* *Go:* {apm-go-ref}/configuration.html#config-environment[`ELASTIC_APM_ENVIRONMENT`] -* *iOS agent:* _Not yet supported_ -* *Java:* {apm-java-ref}/config-core.html#config-environment[`environment`] -* *.NET:* {apm-dotnet-ref}/config-core.html#config-environment[`Environment`] -* *Node.js:* {apm-node-ref}/configuration.html#environment[`environment`] -* *PHP:* {apm-php-ref}/configuration-reference.html#config-environment[`environment`] -* *Python:* {apm-py-ref}/configuration.html#config-environment[`environment`] -* *Ruby:* {apm-ruby-ref}/configuration.html#config-environment[`environment`] -* *Real User Monitoring:* {apm-rum-ref}/configuration.html#environment[`environment`] diff --git a/docs/apm/getting-started.asciidoc b/docs/apm/getting-started.asciidoc deleted file mode 100644 index 1a369202788e1..0000000000000 --- a/docs/apm/getting-started.asciidoc +++ /dev/null @@ -1,71 +0,0 @@ -[role="xpack"] -[[apm-getting-started]] -== Get started with the APM app - -++++ -<titleabbrev>Get started</titleabbrev> -++++ - -// Conditionally display a screenshot or video depending on what the -// current documentation version is. - -ifeval::["{is-current-version}"=="true"] -++++ -<script type="text/javascript" async src="https://play.vidyard.com/embed/v4.js"></script> -<img - style="width: 100%; margin: auto; display: block;" - class="vidyard-player-embed" - src="https://play.vidyard.com/Y4nE2XLYEk75odbRQmUA3g.jpg" - data-uuid="Y4nE2XLYEk75odbRQmUA3g" - data-v="4" - data-type="inline" -/> -</br> -++++ -endif::[] - -For a quick, high-level overview of the health and performance of your application, -start with: - -* <<services>> -* <<traces>> -* <<dependencies>> -* <<service-maps>> - -Notice something awry? Select a service or trace and dive deeper with: - -* <<service-overview>> -* <<mobile-service-overview>> -* <<transactions>> -* <<spans>> -* <<errors>> -* <<metrics>> -* <<infrastructure>> -* <<logs>> - -TIP: Want to learn more about the Elastic APM ecosystem? -See the {apm-guide-ref}/apm-overview.html[APM Overview]. - -include::services.asciidoc[] - -include::traces.asciidoc[] - -include::dependencies.asciidoc[] - -include::service-maps.asciidoc[] - -include::service-overview.asciidoc[] - -include::mobile-service.asciidoc[] - -include::transactions.asciidoc[] - -include::spans.asciidoc[] - -include::errors.asciidoc[] - -include::metrics.asciidoc[] - -include::infrastructure.asciidoc[] - -include::logs.asciidoc[] \ No newline at end of file diff --git a/docs/apm/how-to-guides.asciidoc b/docs/apm/how-to-guides.asciidoc deleted file mode 100644 index fe7d6626a077c..0000000000000 --- a/docs/apm/how-to-guides.asciidoc +++ /dev/null @@ -1,47 +0,0 @@ -[role="xpack"] -[[apm-how-to]] -== How-to guides - -Learn how to perform common APM app tasks. - - -* <<agent-configuration>> -* <<apm-spaces>> -* <<apm-alerts>> -* <<custom-links>> -* <<filters>> -* <<correlations>> -* <<agent-explorer>> -* <<machine-learning-integration>> -* <<mobile-session-explorer>> -* <<apm-lambda>> -* <<advanced-queries>> -* <<storage-explorer>> -* <<transactions-annotations>> - - -include::agent-configuration.asciidoc[] - -include::apm-spaces.asciidoc[] - -include::apm-alerts.asciidoc[] - -include::custom-links.asciidoc[] - -include::filters.asciidoc[] - -include::correlations.asciidoc[] - -include::agent-explorer.asciidoc[] - -include::machine-learning.asciidoc[] - -include::mobile-session-explorer.asciidoc[] - -include::lambda.asciidoc[] - -include::advanced-queries.asciidoc[] - -include::storage-explorer.asciidoc[] - -include::deployment-annotations.asciidoc[] \ No newline at end of file diff --git a/docs/apm/images/active-alert-service.png b/docs/apm/images/active-alert-service.png deleted file mode 100644 index 7d7deaf078ffb..0000000000000 Binary files a/docs/apm/images/active-alert-service.png and /dev/null differ diff --git a/docs/apm/images/add-variable.png b/docs/apm/images/add-variable.png deleted file mode 100644 index 860ab66f22f4e..0000000000000 Binary files a/docs/apm/images/add-variable.png and /dev/null differ diff --git a/docs/apm/images/advanced-discover.png b/docs/apm/images/advanced-discover.png deleted file mode 100644 index 5291526783a6b..0000000000000 Binary files a/docs/apm/images/advanced-discover.png and /dev/null differ diff --git a/docs/apm/images/all-instances.png b/docs/apm/images/all-instances.png deleted file mode 100644 index 70028b5a9b58b..0000000000000 Binary files a/docs/apm/images/all-instances.png and /dev/null differ diff --git a/docs/apm/images/apm-agent-configuration.png b/docs/apm/images/apm-agent-configuration.png deleted file mode 100644 index 22fd9d75c3d73..0000000000000 Binary files a/docs/apm/images/apm-agent-configuration.png and /dev/null differ diff --git a/docs/apm/images/apm-agent-explorer-flyout.png b/docs/apm/images/apm-agent-explorer-flyout.png deleted file mode 100644 index 9792a93a71234..0000000000000 Binary files a/docs/apm/images/apm-agent-explorer-flyout.png and /dev/null differ diff --git a/docs/apm/images/apm-agent-explorer.png b/docs/apm/images/apm-agent-explorer.png deleted file mode 100644 index c6fb7d031ed92..0000000000000 Binary files a/docs/apm/images/apm-agent-explorer.png and /dev/null differ diff --git a/docs/apm/images/apm-alert.png b/docs/apm/images/apm-alert.png deleted file mode 100644 index ccaf2de64ec08..0000000000000 Binary files a/docs/apm/images/apm-alert.png and /dev/null differ diff --git a/docs/apm/images/apm-anomaly-alert.png b/docs/apm/images/apm-anomaly-alert.png deleted file mode 100644 index 35ce9a2296c9c..0000000000000 Binary files a/docs/apm/images/apm-anomaly-alert.png and /dev/null differ diff --git a/docs/apm/images/apm-distributed-tracing.png b/docs/apm/images/apm-distributed-tracing.png deleted file mode 100644 index 4d1b8cde20e95..0000000000000 Binary files a/docs/apm/images/apm-distributed-tracing.png and /dev/null differ diff --git a/docs/apm/images/apm-error-group.png b/docs/apm/images/apm-error-group.png deleted file mode 100644 index 22bceb9d8111b..0000000000000 Binary files a/docs/apm/images/apm-error-group.png and /dev/null differ diff --git a/docs/apm/images/apm-errors-overview.png b/docs/apm/images/apm-errors-overview.png deleted file mode 100644 index c390b7ddc009d..0000000000000 Binary files a/docs/apm/images/apm-errors-overview.png and /dev/null differ diff --git a/docs/apm/images/apm-errors-watcher-assistant.png b/docs/apm/images/apm-errors-watcher-assistant.png deleted file mode 100644 index 1a4d6b5b4c0ea..0000000000000 Binary files a/docs/apm/images/apm-errors-watcher-assistant.png and /dev/null differ diff --git a/docs/apm/images/apm-geo-ui.png b/docs/apm/images/apm-geo-ui.png deleted file mode 100644 index 69c1390a27989..0000000000000 Binary files a/docs/apm/images/apm-geo-ui.png and /dev/null differ diff --git a/docs/apm/images/apm-index-pattern.png b/docs/apm/images/apm-index-pattern.png deleted file mode 100644 index b389110fc422f..0000000000000 Binary files a/docs/apm/images/apm-index-pattern.png and /dev/null differ diff --git a/docs/apm/images/apm-integration-config.png b/docs/apm/images/apm-integration-config.png deleted file mode 100644 index 7ff5cb5e9d0ba..0000000000000 Binary files a/docs/apm/images/apm-integration-config.png and /dev/null differ diff --git a/docs/apm/images/apm-logs-tab.png b/docs/apm/images/apm-logs-tab.png deleted file mode 100644 index c79be8b5eb0b7..0000000000000 Binary files a/docs/apm/images/apm-logs-tab.png and /dev/null differ diff --git a/docs/apm/images/apm-metrics.png b/docs/apm/images/apm-metrics.png deleted file mode 100644 index c2d609c7c4cd5..0000000000000 Binary files a/docs/apm/images/apm-metrics.png and /dev/null differ diff --git a/docs/apm/images/apm-ml-integration.png b/docs/apm/images/apm-ml-integration.png deleted file mode 100644 index e95dae5a41f35..0000000000000 Binary files a/docs/apm/images/apm-ml-integration.png and /dev/null differ diff --git a/docs/apm/images/apm-query-bar.png b/docs/apm/images/apm-query-bar.png deleted file mode 100644 index 457573f485f5a..0000000000000 Binary files a/docs/apm/images/apm-query-bar.png and /dev/null differ diff --git a/docs/apm/images/apm-roles-config.png b/docs/apm/images/apm-roles-config.png deleted file mode 100644 index ebd992abe9303..0000000000000 Binary files a/docs/apm/images/apm-roles-config.png and /dev/null differ diff --git a/docs/apm/images/apm-service-group.png b/docs/apm/images/apm-service-group.png deleted file mode 100644 index 44a0191411e3e..0000000000000 Binary files a/docs/apm/images/apm-service-group.png and /dev/null differ diff --git a/docs/apm/images/apm-service-map-anomaly.png b/docs/apm/images/apm-service-map-anomaly.png deleted file mode 100644 index cd59f86690666..0000000000000 Binary files a/docs/apm/images/apm-service-map-anomaly.png and /dev/null differ diff --git a/docs/apm/images/apm-services-overview.png b/docs/apm/images/apm-services-overview.png deleted file mode 100644 index 0badeea3be4b3..0000000000000 Binary files a/docs/apm/images/apm-services-overview.png and /dev/null differ diff --git a/docs/apm/images/apm-services-trace.png b/docs/apm/images/apm-services-trace.png deleted file mode 100644 index 083c69318e8ed..0000000000000 Binary files a/docs/apm/images/apm-services-trace.png and /dev/null differ diff --git a/docs/apm/images/apm-settings.png b/docs/apm/images/apm-settings.png deleted file mode 100644 index 2c8ebace287b8..0000000000000 Binary files a/docs/apm/images/apm-settings.png and /dev/null differ diff --git a/docs/apm/images/apm-setup.png b/docs/apm/images/apm-setup.png deleted file mode 100644 index 8aadd8911c6e8..0000000000000 Binary files a/docs/apm/images/apm-setup.png and /dev/null differ diff --git a/docs/apm/images/apm-span-detail.png b/docs/apm/images/apm-span-detail.png deleted file mode 100644 index d0b6a4de3d3df..0000000000000 Binary files a/docs/apm/images/apm-span-detail.png and /dev/null differ diff --git a/docs/apm/images/apm-traces.png b/docs/apm/images/apm-traces.png deleted file mode 100644 index c8b8d40b01335..0000000000000 Binary files a/docs/apm/images/apm-traces.png and /dev/null differ diff --git a/docs/apm/images/apm-transaction-annotation.png b/docs/apm/images/apm-transaction-annotation.png deleted file mode 100644 index b9360db2ff474..0000000000000 Binary files a/docs/apm/images/apm-transaction-annotation.png and /dev/null differ diff --git a/docs/apm/images/apm-transaction-duration-dist.png b/docs/apm/images/apm-transaction-duration-dist.png deleted file mode 100644 index 9c7ab5dd67dc0..0000000000000 Binary files a/docs/apm/images/apm-transaction-duration-dist.png and /dev/null differ diff --git a/docs/apm/images/apm-transaction-response-dist.png b/docs/apm/images/apm-transaction-response-dist.png deleted file mode 100644 index 70e5ad7041287..0000000000000 Binary files a/docs/apm/images/apm-transaction-response-dist.png and /dev/null differ diff --git a/docs/apm/images/apm-transaction-sample.png b/docs/apm/images/apm-transaction-sample.png deleted file mode 100644 index a9490fc20d853..0000000000000 Binary files a/docs/apm/images/apm-transaction-sample.png and /dev/null differ diff --git a/docs/apm/images/apm-transactions-overview.png b/docs/apm/images/apm-transactions-overview.png deleted file mode 100644 index 34cd0219b895d..0000000000000 Binary files a/docs/apm/images/apm-transactions-overview.png and /dev/null differ diff --git a/docs/apm/images/apm-transactions-table.png b/docs/apm/images/apm-transactions-table.png deleted file mode 100644 index 8a3415bc9a9f1..0000000000000 Binary files a/docs/apm/images/apm-transactions-table.png and /dev/null differ diff --git a/docs/apm/images/correlations-failed-transactions.png b/docs/apm/images/correlations-failed-transactions.png deleted file mode 100644 index 19221e751ef69..0000000000000 Binary files a/docs/apm/images/correlations-failed-transactions.png and /dev/null differ diff --git a/docs/apm/images/correlations-hover.png b/docs/apm/images/correlations-hover.png deleted file mode 100644 index 9731517b32c43..0000000000000 Binary files a/docs/apm/images/correlations-hover.png and /dev/null differ diff --git a/docs/apm/images/create-github-issue.png b/docs/apm/images/create-github-issue.png deleted file mode 100644 index 81ea4e5e78c27..0000000000000 Binary files a/docs/apm/images/create-github-issue.png and /dev/null differ diff --git a/docs/apm/images/create-jira-issue.png b/docs/apm/images/create-jira-issue.png deleted file mode 100644 index 962c98df3f6c6..0000000000000 Binary files a/docs/apm/images/create-jira-issue.png and /dev/null differ diff --git a/docs/apm/images/dependencies-drilldown.png b/docs/apm/images/dependencies-drilldown.png deleted file mode 100644 index af82ee3d9305f..0000000000000 Binary files a/docs/apm/images/dependencies-drilldown.png and /dev/null differ diff --git a/docs/apm/images/dependencies.png b/docs/apm/images/dependencies.png deleted file mode 100644 index 260025d31654b..0000000000000 Binary files a/docs/apm/images/dependencies.png and /dev/null differ diff --git a/docs/apm/images/dynamic-config.svg b/docs/apm/images/dynamic-config.svg deleted file mode 100644 index df62a3c84f4b4..0000000000000 --- a/docs/apm/images/dynamic-config.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="59" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="59" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#9f9f9f" d="M0 0h0v20H0z"/><path fill="#9f9f9f" d="M0 0h59v20H0z"/><path fill="url(#b)" d="M0 0h59v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="295" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">Dynamic</text><text x="295" y="140" transform="scale(.1)" textLength="490">Dynamic</text></g> </svg> \ No newline at end of file diff --git a/docs/apm/images/error-rate.png b/docs/apm/images/error-rate.png deleted file mode 100644 index 845fa2af07de1..0000000000000 Binary files a/docs/apm/images/error-rate.png and /dev/null differ diff --git a/docs/apm/images/example-metadata.png b/docs/apm/images/example-metadata.png deleted file mode 100644 index 2a5bda7f088f6..0000000000000 Binary files a/docs/apm/images/example-metadata.png and /dev/null differ diff --git a/docs/apm/images/global-filters.png b/docs/apm/images/global-filters.png deleted file mode 100644 index f93a5214c316b..0000000000000 Binary files a/docs/apm/images/global-filters.png and /dev/null differ diff --git a/docs/apm/images/green-service.png b/docs/apm/images/green-service.png deleted file mode 100644 index bbc00a3543b08..0000000000000 Binary files a/docs/apm/images/green-service.png and /dev/null differ diff --git a/docs/apm/images/infra.png b/docs/apm/images/infra.png deleted file mode 100644 index e139012270d5f..0000000000000 Binary files a/docs/apm/images/infra.png and /dev/null differ diff --git a/docs/apm/images/jvm-metrics-overview.png b/docs/apm/images/jvm-metrics-overview.png deleted file mode 100644 index c6f28f7bdf48f..0000000000000 Binary files a/docs/apm/images/jvm-metrics-overview.png and /dev/null differ diff --git a/docs/apm/images/jvm-metrics.png b/docs/apm/images/jvm-metrics.png deleted file mode 100644 index 70f7965b72df6..0000000000000 Binary files a/docs/apm/images/jvm-metrics.png and /dev/null differ diff --git a/docs/apm/images/lambda-cold-start-trace.png b/docs/apm/images/lambda-cold-start-trace.png deleted file mode 100644 index c6f6efd0557ce..0000000000000 Binary files a/docs/apm/images/lambda-cold-start-trace.png and /dev/null differ diff --git a/docs/apm/images/lambda-correlations.png b/docs/apm/images/lambda-correlations.png deleted file mode 100644 index c1a72ccb2d930..0000000000000 Binary files a/docs/apm/images/lambda-correlations.png and /dev/null differ diff --git a/docs/apm/images/lambda-overview.png b/docs/apm/images/lambda-overview.png deleted file mode 100644 index 9d0558949f0ce..0000000000000 Binary files a/docs/apm/images/lambda-overview.png and /dev/null differ diff --git a/docs/apm/images/latency.png b/docs/apm/images/latency.png deleted file mode 100644 index 1c220c1a4bfdd..0000000000000 Binary files a/docs/apm/images/latency.png and /dev/null differ diff --git a/docs/apm/images/local-filter.png b/docs/apm/images/local-filter.png deleted file mode 100644 index edcaf8b6a609c..0000000000000 Binary files a/docs/apm/images/local-filter.png and /dev/null differ diff --git a/docs/apm/images/logs.png b/docs/apm/images/logs.png deleted file mode 100644 index 94d77b47495f1..0000000000000 Binary files a/docs/apm/images/logs.png and /dev/null differ diff --git a/docs/apm/images/metadata-icons.png b/docs/apm/images/metadata-icons.png deleted file mode 100644 index 402c0ed07c70d..0000000000000 Binary files a/docs/apm/images/metadata-icons.png and /dev/null differ diff --git a/docs/apm/images/mobile-location.png b/docs/apm/images/mobile-location.png deleted file mode 100644 index 35b0d91a2d2bf..0000000000000 Binary files a/docs/apm/images/mobile-location.png and /dev/null differ diff --git a/docs/apm/images/mobile-most-used.png b/docs/apm/images/mobile-most-used.png deleted file mode 100644 index 24b29f95d4742..0000000000000 Binary files a/docs/apm/images/mobile-most-used.png and /dev/null differ diff --git a/docs/apm/images/mobile-session-error-details.png b/docs/apm/images/mobile-session-error-details.png deleted file mode 100644 index 41c0bf509514d..0000000000000 Binary files a/docs/apm/images/mobile-session-error-details.png and /dev/null differ diff --git a/docs/apm/images/mobile-session-explorer-apm.png b/docs/apm/images/mobile-session-explorer-apm.png deleted file mode 100644 index 55fbc857901f0..0000000000000 Binary files a/docs/apm/images/mobile-session-explorer-apm.png and /dev/null differ diff --git a/docs/apm/images/mobile-session-explorer-nav.png b/docs/apm/images/mobile-session-explorer-nav.png deleted file mode 100644 index d208f4091201a..0000000000000 Binary files a/docs/apm/images/mobile-session-explorer-nav.png and /dev/null differ diff --git a/docs/apm/images/mobile-session-filter-discover.png b/docs/apm/images/mobile-session-filter-discover.png deleted file mode 100644 index 989284ba2aeac..0000000000000 Binary files a/docs/apm/images/mobile-session-filter-discover.png and /dev/null differ diff --git a/docs/apm/images/mobile-tp.png b/docs/apm/images/mobile-tp.png deleted file mode 100644 index 81ce267fb858e..0000000000000 Binary files a/docs/apm/images/mobile-tp.png and /dev/null differ diff --git a/docs/apm/images/operations-detail.png b/docs/apm/images/operations-detail.png deleted file mode 100644 index 64a1c6550859d..0000000000000 Binary files a/docs/apm/images/operations-detail.png and /dev/null differ diff --git a/docs/apm/images/operations.png b/docs/apm/images/operations.png deleted file mode 100644 index 119f8bdf99ff7..0000000000000 Binary files a/docs/apm/images/operations.png and /dev/null differ diff --git a/docs/apm/images/red-service.png b/docs/apm/images/red-service.png deleted file mode 100644 index be7a62b1774ab..0000000000000 Binary files a/docs/apm/images/red-service.png and /dev/null differ diff --git a/docs/apm/images/service-maps-java.png b/docs/apm/images/service-maps-java.png deleted file mode 100644 index aa8e5dc5056cd..0000000000000 Binary files a/docs/apm/images/service-maps-java.png and /dev/null differ diff --git a/docs/apm/images/service-maps.png b/docs/apm/images/service-maps.png deleted file mode 100644 index 3d8d48bd957ae..0000000000000 Binary files a/docs/apm/images/service-maps.png and /dev/null differ diff --git a/docs/apm/images/service-quick-health.png b/docs/apm/images/service-quick-health.png deleted file mode 100644 index aab1332513079..0000000000000 Binary files a/docs/apm/images/service-quick-health.png and /dev/null differ diff --git a/docs/apm/images/spans-dependencies.png b/docs/apm/images/spans-dependencies.png deleted file mode 100644 index 558099dd450c1..0000000000000 Binary files a/docs/apm/images/spans-dependencies.png and /dev/null differ diff --git a/docs/apm/images/specific-transaction-search.png b/docs/apm/images/specific-transaction-search.png deleted file mode 100644 index 4ed548f015713..0000000000000 Binary files a/docs/apm/images/specific-transaction-search.png and /dev/null differ diff --git a/docs/apm/images/specific-transaction.png b/docs/apm/images/specific-transaction.png deleted file mode 100644 index 52073bf76520a..0000000000000 Binary files a/docs/apm/images/specific-transaction.png and /dev/null differ diff --git a/docs/apm/images/storage-explorer-expanded.png b/docs/apm/images/storage-explorer-expanded.png deleted file mode 100644 index 844f07b6f8b7e..0000000000000 Binary files a/docs/apm/images/storage-explorer-expanded.png and /dev/null differ diff --git a/docs/apm/images/storage-explorer-overview.png b/docs/apm/images/storage-explorer-overview.png deleted file mode 100644 index d3fcccca5f6ee..0000000000000 Binary files a/docs/apm/images/storage-explorer-overview.png and /dev/null differ diff --git a/docs/apm/images/time-series-expected-bounds-comparison.png b/docs/apm/images/time-series-expected-bounds-comparison.png deleted file mode 100644 index 6e705064e65b3..0000000000000 Binary files a/docs/apm/images/time-series-expected-bounds-comparison.png and /dev/null differ diff --git a/docs/apm/images/trace-explorer.png b/docs/apm/images/trace-explorer.png deleted file mode 100644 index 70c13f650e2fb..0000000000000 Binary files a/docs/apm/images/trace-explorer.png and /dev/null differ diff --git a/docs/apm/images/traffic-transactions.png b/docs/apm/images/traffic-transactions.png deleted file mode 100644 index 05e66dfaa4ece..0000000000000 Binary files a/docs/apm/images/traffic-transactions.png and /dev/null differ diff --git a/docs/apm/images/transaction-icon.png b/docs/apm/images/transaction-icon.png deleted file mode 100644 index 3534e6915d6cb..0000000000000 Binary files a/docs/apm/images/transaction-icon.png and /dev/null differ diff --git a/docs/apm/images/yellow-service.png b/docs/apm/images/yellow-service.png deleted file mode 100644 index 43afd6250be72..0000000000000 Binary files a/docs/apm/images/yellow-service.png and /dev/null differ diff --git a/docs/apm/index.asciidoc b/docs/apm/index.asciidoc deleted file mode 100644 index 2674ecf2a13b8..0000000000000 --- a/docs/apm/index.asciidoc +++ /dev/null @@ -1,41 +0,0 @@ -[role="xpack"] -[[xpack-apm]] -= APM - -[partintro] --- -The APM app in {kib} allows you to monitor your software services and applications in real-time; -visualize detailed performance information on your services, -identify and analyze errors, -and monitor host-level and APM agent-specific metrics like JVM and Go runtime metrics. - -[float] -[[apm-bottlenecks]] -== Visualizing application bottlenecks - -Having access to application-level insights with just a few clicks can drastically decrease the time you spend -debugging errors, slow response times, and crashes. - -For example, you can see information about response times, requests per minute, and status codes per endpoint. -You can even dive into a specific request sample and get a complete waterfall view of what your application is spending its time on. -You might see that your bottlenecks are in database queries, cache calls, or external requests. -For each incoming request and each application error, -you can also see contextual information such as the request header, user information, -system values, or custom data that you manually attached to the request. --- - -:beat_kib_app: APM and User Experience - -include::set-up.asciidoc[] - -include::getting-started.asciidoc[] - -include::how-to-guides.asciidoc[] - -include::apm-app-users.asciidoc[] - -include::settings.asciidoc[] - -include::api.asciidoc[] - -include::troubleshooting.asciidoc[] diff --git a/docs/apm/infrastructure.asciidoc b/docs/apm/infrastructure.asciidoc deleted file mode 100644 index ff6343061ca24..0000000000000 --- a/docs/apm/infrastructure.asciidoc +++ /dev/null @@ -1,15 +0,0 @@ -[role="xpack"] -[[infrastructure]] -=== Infrastructure - -beta::[] - -The *Infrastructure* tab provides information about the containers, pods, and hosts -that the selected service is linked to. - -[role="screenshot"] -image::apm/images/infra.png[Example view of the Infrastructure tab in APM app in Kibana] - -IT ops and software reliability engineers (SREs) can use this tab -to quickly find a service's underlying infrastructure resources when debugging a problem. -Knowing what infrastructure is related to a service allows you to remediate issues by restarting, killing hanging instances, changing configuration, rolling back deployments, scaling up, scaling out, and so on. \ No newline at end of file diff --git a/docs/apm/lambda.asciidoc b/docs/apm/lambda.asciidoc deleted file mode 100644 index af3012dd19a5e..0000000000000 --- a/docs/apm/lambda.asciidoc +++ /dev/null @@ -1,52 +0,0 @@ -[role="xpack"] -[[apm-lambda]] -=== Observe Lambda functions - -Elastic APM provides performance and error monitoring for AWS Lambda functions. -See how your Lambda functions relate to and depend on other services, and -get insight into function execution and runtime behavior, like lambda duration, cold start rate, cold start duration, compute usage, memory usage, and more. - -To set up Lambda monitoring, see the relevant -{apm-guide-ref}/monitoring-aws-lambda.html[quick start guide]. - -[role="screenshot"] -image::apm/images/lambda-overview.png[lambda overview] - -[float] -[[apm-lambda-cold-start-info]] -==== Cold starts - -A cold start occurs when a Lambda function has not been used for a certain period of time. A lambda worker receives a request to run the function and prepares an execution environment. - -Cold starts are an unavoidable byproduct of the serverless world, but visibility into how they impact your services can help you make better decisions about factors like how much memory to allocate to a function, whether to enable provisioned concurrency, or if it's time to consider removing a large dependency. - -[float] -[[apm-lambda-cold-start-rate]] -===== Cold start rate - -The cold start rate (i.e. proportion of requests that experience a cold start) is displayed per service and per transaction. - -Cold start is also displayed in the trace waterfall, where you can drill-down into individual traces and see trace metadata like AWS request ID, trigger type, and trigger request ID. - -[role="screenshot"] -image::apm/images/lambda-cold-start-trace.png[lambda cold start trace] - -[float] -[[apm-lambda-cold-start-latency]] -===== Latency distribution correlation - -The <<correlations-latency,latency correlations>> feature can be used to visualize the impact of Lambda cold starts on latency--just select the `faas.coldstart` field. - -[role="screenshot"] -image::apm/images/lambda-correlations.png[lambda correlations example] - -[float] -[[apm-lambda-service-config]] -==== AWS Lambda function grouping - -The default APM agent configuration results in one APM service per AWS Lambda function, -where the Lambda function name is the service name. - -In some use cases, it makes more sense to logically group multiple lambda functions under a single -APM service. You can achieve this by setting the `ELASTIC_APM_SERVICE_NAME` environment variable -on related Lambda functions to the same value. diff --git a/docs/apm/logs.asciidoc b/docs/apm/logs.asciidoc deleted file mode 100644 index 04f5eff185692..0000000000000 --- a/docs/apm/logs.asciidoc +++ /dev/null @@ -1,19 +0,0 @@ -[role="xpack"] -[[logs]] -=== Logs - -The *Logs* tab shows contextual logs for the selected service. - -// tag::log-overview[] -Logs provide detailed information about specific events, and are crucial to successfully debugging slow or erroneous transactions. - -If you've correlated your application's logs and traces, you never have to search for relevant data; it's already available to you. Viewing log and trace data together allows you to quickly diagnose and solve problems. - -To learn how to correlate your logs with your instrumented services, -see {observability-guide}/application-logs.html[log correlation] -// end::log-overview[] - -[role="screenshot"] -image::apm/images/logs.png[Example view of the Logs tab in APM app in Kibana] - -TIP: Logs displayed on this page are filtered on `service.name` diff --git a/docs/apm/machine-learning.asciidoc b/docs/apm/machine-learning.asciidoc deleted file mode 100644 index f01cdf70b6e05..0000000000000 --- a/docs/apm/machine-learning.asciidoc +++ /dev/null @@ -1,73 +0,0 @@ -[role="xpack"] -[[machine-learning-integration]] -=== Machine learning integration - -++++ -<titleabbrev>Integrate with machine learning</titleabbrev> -++++ - -The Machine learning integration initiates a new job predefined to calculate anomaly scores on APM transaction durations. -With this integration, you can quickly pinpoint anomalous transactions and see the health of -any upstream and downstream services. - -Machine learning jobs are created per environment and are based on a service's average response time. -Because jobs are created at the environment level, -you can add new services to your existing environments without the need for additional machine learning jobs. - -Results from machine learning jobs are shown in multiple places throughout the APM app: - -* The **Services overview** provides a quick-glance view of the general health of all of your services. -+ -[role="screenshot"] -image::apm/images/service-quick-health.png[Example view of anomaly scores on response times in the APM app] - -* The transaction duration chart will show the expected bounds and add an annotation when the anomaly score is 75 or above. -+ -[role="screenshot"] -image::apm/images/apm-ml-integration.png[Example view of anomaly scores on response times in the APM app] - -* Service Maps will display a color-coded anomaly indicator based on the detected anomaly score. -+ -[role="screenshot"] -image::apm/images/apm-service-map-anomaly.png[Example view of anomaly scores on service maps in the APM app] - -[float] -[[create-ml-integration]] -=== Enable anomaly detection - -To enable machine learning anomaly detection: - -. From the Services overview, Traces overview, or Service Map tab, -select **Anomaly detection**. - -. Click **Create Job**. - -. Machine learning jobs are created at the environment level. -Select all of the service environments that you want to enable anomaly detection in. -Anomalies will surface for all services and transaction types within the selected environments. - -. Click **Create Jobs**. - -That's it! After a few minutes, the job will begin calculating results; -it might take additional time for results to appear on your service maps. -To manage existing jobs, click **Manage jobs**. - -[float] -[[warning-ml-integration]] -=== Anomaly detection warning - -To make machine learning as easy as possible to set up, -the APM app will warn you when filtered to an environment without a machine learning job. - -[role="screenshot"] -image::apm/images/apm-anomaly-alert.png[Example view of anomaly alert in the APM app] - -[float] -[[unkown-ml-integration]] -=== Unknown service health - -After enabling anomaly detection, service health may display as "Unknown". Here are some reasons why this can occur: - -1. No machine learning job exists. See <<create-ml-integration>> to enable anomaly detection and create a machine learning job. -2. There is no machine learning data for the job. If you just created the machine learning job you'll need to wait a few minutes for data to be available. Alternatively, if the service or its enviroment are new, you'll need to wait for more trace data. -3. No "request" or "page-load" transaction type exists for this service; service health is only available for these transaction types. diff --git a/docs/apm/metrics.asciidoc b/docs/apm/metrics.asciidoc deleted file mode 100644 index c8f2a9c552720..0000000000000 --- a/docs/apm/metrics.asciidoc +++ /dev/null @@ -1,24 +0,0 @@ -[role="xpack"] -[[metrics]] -=== Metrics - -The *Metrics* overview provides APM agent-specific metrics, -which lets you perform more in-depth root cause analysis investigations within the APM app. - -If you're experiencing a problem with your service, you can use this page to attempt to find the underlying cause. -For example, you might be able to correlate a high number of errors with a long transaction duration, high CPU usage, or a memory leak. - -[role="screenshot"] -image::apm/images/apm-metrics.png[Example view of the Metrics overview in APM app in Kibana] - -If you're using the Java APM agent, you can view metrics for each JVM. - -[role="screenshot"] -image::apm/images/jvm-metrics-overview.png[Example view of the Metrics overview for the Java Agent] - -Breaking down metrics by JVM makes it much easier to analyze the provided metrics: -CPU usage, memory usage, heap or non-heap memory, -thread count, garbage collection rate, and garbage collection time spent per minute. - -[role="screenshot"] -image::apm/images/jvm-metrics.png[Example view of the Metrics overview for the Java Agent] diff --git a/docs/apm/mobile-errors.asciidoc b/docs/apm/mobile-errors.asciidoc deleted file mode 100644 index df4e4f380b0c7..0000000000000 --- a/docs/apm/mobile-errors.asciidoc +++ /dev/null @@ -1,36 +0,0 @@ -[role="xpack"] -[[mobile-errors-crashes]] -=== Mobile errors and crashes - -TIP: {apm-guide-ref}/data-model-errors.html[Errors] are groups of exceptions with a similar exception or log message. - -The *Errors & Crashes* overview provides a high-level view of errors and crashes that APM mobile agents catch, -or that users manually report with APM agent APIs. Errors and crashes are separated into two tabs for easy differentiation. -Like errors are grouped together to make it easy to quickly see which errors are affecting your services, -and to take actions to rectify them. - - - - - -[role="screenshot"] -image::apm/images/mobile-errors-overview.png[Mobile Errors overview] - -Selecting an error group ID or error message brings you to the *Error group*. - -[role="screenshot"] -image::apm/images/mobile-error-group.png[Mobile Error group] - -The error group details page visualizes the number of error occurrences over time and compared to a recent time range. -This allows you to quickly determine if the error rate is changing or remaining constant. -You'll also see the "most affected" chart which can be oriented to 'by device' or 'by app version'. - -Further down, you'll see an Error sample. -The error shown is always the most recent to occur. -The sample includes the exception message, culprit, stack trace where the error occurred (when available), -and additional contextual information to help debug the issue--all of which can be copied with the click of a button. - -In some cases, you might also see a Transaction sample ID. -This feature allows you to make a connection between the errors and transactions, -by linking you to the specific transaction where the error occurred. -This allows you to see the whole trace, including which services the request went through. diff --git a/docs/apm/mobile-service.asciidoc b/docs/apm/mobile-service.asciidoc deleted file mode 100644 index 774d4592dd67a..0000000000000 --- a/docs/apm/mobile-service.asciidoc +++ /dev/null @@ -1,61 +0,0 @@ -[role="xpack"] -[[mobile-service-overview]] -=== Mobile service overview - -Selecting a mobile service brings you to the *Mobile service overview*. -The *Mobile service overview* contains a wide variety of charts and tables that provide -high-level visibility into how a mobile service is performing for your users--enabling you -to make data-driven decisions about how to improve your user experience. - -For example, see: - -* Crash Rate (Crashes per session) -* Slowest App load time -- coming soon -* Number of sessions -* Number of HTTP requests -* Map showing the total number of HTTP requests based on country and region -* Most used devices, network connection type, OS version, and app version -* Latency, throughput, and errors over time -* Service dependencies - -All of these metrics & insights can help SREs and developers better understand the health -of their mobile application environment and the impact of backend errors and bottlenecks on end-user experience. - -[discrete] -[[mobile-service-stats]] -=== Quick stats - -Understand the impact of slow application load times and variations in application crash rate on user traffic (coming soon). -Visualize session and HTTP trends, and see where your users are located--enabling you to optimize your infrastructure deployment and routing topology. - -Note: due to the way crash rate is calculated (crashes per session) it is possible to have greater than 100% rate, due to fact that a session may contain multiple crashes. - -[role="screenshot"] -image::apm/images/mobile-location.png[mobile service overview centered on location map] - -[discrete] -[[mobile-service-most-used]] -=== Most used - -Optimize your end-user experience and your application QA strategy based on your most used device models and operating system versions. - -[role="screenshot"] -image::apm/images/mobile-most-used.png[mobile service overview showing most used devices, network, OS, and app version] - - -[discrete] -[[mobile-throughput-transactions]] -=== Throughput and transactions - -include::./service-overview.asciidoc[tag=throughput-transactions] - -[discrete] -[[mobile-error-and-dependencies]] -=== Failed transaction rate and dependencies - -include::./service-overview.asciidoc[tag=ftr] - -include::./service-overview.asciidoc[tag=dependencies] - -[role="screenshot"] -image::apm/images/mobile-tp.png[mobile service overview showing latency, throughput, and errors] \ No newline at end of file diff --git a/docs/apm/mobile-session-explorer.asciidoc b/docs/apm/mobile-session-explorer.asciidoc deleted file mode 100644 index 92d4e4dc1fe8d..0000000000000 --- a/docs/apm/mobile-session-explorer.asciidoc +++ /dev/null @@ -1,43 +0,0 @@ -[role="xpack] -[[mobile-session-explorer]] -=== Exploring mobile sessions with Discover -Elastic Mobile APM provides session tracking by attaching a `session.id`, a guid, to every span and event. -This allows for the recall of the activities of a specific user during a specific period of time. The best way recall -these data points is using the xref:document-explorer[Discover document explorer]. This guide will explain how to do that. - -=== Viewing sessions with Discover - -The first step is to find the relevant `session.id`. In this example, we'll walk through investigating a crash. -Since all events and spans have `session.id` attributes, a crash is no different. - -The steps to follow are: - -* copy the `session.id` from the relevant document. -* Open the Discover page. -* Select the appropriate data view (use `APM` to search all datastreams) -* set filter to the copied `session.id` - -Here we can see the `session.id` guid in the metadata viewer in the error detail view: -[role="screenshot"] -image::images/mobile-session-error-details.png[Example of session.id in error details] - -Copy this value and open the Discover page: - -[role="screenshot"] -image::images/mobile-session-explorer-nav.png[Example view of navigation to Discover] - - -set the data view. `APM` selected in the example: - -[role="screenshot"] -image::images/mobile-session-explorer-apm.png[Example view of Explorer selecting APM data view] - -filter using the `session.id`: `session.id: "<copied session id guid>"`: - -[role="screenshot"] -image::images/mobile-session-filter-discover.png[Filter Explor using session.id] - -explore all the documents associated with that session id including crashes, lifecycle events, network requests, errors, and other custom events! - - - diff --git a/docs/apm/service-maps.asciidoc b/docs/apm/service-maps.asciidoc deleted file mode 100644 index 85c8efa4adb5d..0000000000000 --- a/docs/apm/service-maps.asciidoc +++ /dev/null @@ -1,118 +0,0 @@ -[role="xpack"] -[[service-maps]] -=== Service Map - -A service map is a real-time visual representation of the instrumented services in your application's architecture. -It shows you how these services are connected, along with high-level metrics like average transaction duration, -requests per minute, and errors per minute. -If enabled, service maps also integrate with machine learning--for real time health indicators based on anomaly detection scores. -All of these features can help you quickly and visually assess your services' status and health. - -// Conditionally display a screenshot or video depending on what the -// current documentation version is. - -ifeval::["{is-current-version}"=="true"] -++++ -<script type="text/javascript" async src="https://play.vidyard.com/embed/v4.js"></script> -<img - style="width: 100%; margin: auto; display: block;" - class="vidyard-player-embed" - src="https://play.vidyard.com/VH8gKnPE3Z2csACZTCeQrw.jpg" - data-uuid="VH8gKnPE3Z2csACZTCeQrw" - data-v="4" - data-type="inline" -/> -</br> -++++ -endif::[] - -ifeval::["{is-current-version}"=="false"] -[role="screenshot"] -image::apm/images/service-maps.png[Example view of service maps in the APM app in Kibana] -endif::[] - -We currently surface two types of service maps: - -* Global: All services instrumented with APM agents and the connections between them are shown. -* Service-specific: Highlight connections for a selected service. - -[float] -[[service-maps-how]] -=== How do service maps work? - -Service Maps rely on distributed traces to draw connections between services. -As {apm-guide-ref}/apm-distributed-tracing.html[distributed tracing] is enabled out-of-the-box for supported technologies, so are service maps. -However, if a service isn't instrumented, -or a `traceparent` header isn't being propagated to it, -distributed tracing will not work, and the connection will not be drawn on the map. - -[float] -[[visualize-your-architecture]] -=== Visualize your architecture - -Select the **Service Map** tab to get started. -By default, all instrumented services and connections are shown. -Whether you're onboarding a new engineer, or just trying to grasp the big picture, -drag things around, zoom in and out, and begin to visualize how your services are connected. - -Customize what the service map displays using either the query bar or the environment selector. -The query bar enables you to use <<advanced-queries,advanced queries>> to customize the service map based on your needs. -The environment selector allows you to narrow displayed results to a specific environment. -This can be useful if you have two or more services, in separate environments, but with the same name. -Use the environment drop-down to only see the data you're interested in, like `dev` or `production`. - -If there's a specific service that interests you, select that service to highlight its connections. -Click **Focus map** to refocus the map on the selected service and lock the connection highlighting. -From here, select **Service Details**, or click the **Transactions** tab to jump to the Transaction overview for the selected service. -You can also use the tabs at the top of the page to easily jump to the **Errors** or **Metrics** overview. - -[role="screenshot"] -image::apm/images/service-maps-java.png[Example view of service maps in the APM app in Kibana] - -[float] -[[service-map-anomaly-detection]] -=== Anomaly detection with machine learning - -You can create machine learning jobs to calculate anomaly scores on APM transaction durations within the selected service. -When these jobs are active, service maps will display a color-coded anomaly indicator based on the detected anomaly score: - -[horizontal] -image:apm/images/green-service.png[APM green service]:: Max anomaly score **≤25**. Service is healthy. -image:apm/images/yellow-service.png[APM yellow service]:: Max anomaly score **26-74**. Anomalous activity detected. Service may be degraded. -image:apm/images/red-service.png[APM red service]:: Max anomaly score **≥75**. Anomalous activity detected. Service is unhealthy. - -[role="screenshot"] -image::apm/images/apm-service-map-anomaly.png[Example view of anomaly scores on service maps in the APM app] - -If an anomaly has been detected, click *View anomalies* to view the anomaly detection metric viewer in the Machine learning app. -This time series analysis will display additional details on the severity and time of the detected anomalies. - -To learn how to create a machine learning job, see <<machine-learning-integration,machine learning integration>>. - -[float] -[[service-maps-legend]] -=== Legend - -Nodes appear on the map in one of two shapes: - -* **Circle**: Instrumented services. Interior icons are based on the language of the APM agent used. -* **Diamond**: Databases, external, and messaging. Interior icons represent the generic type, -with specific icons for known entities, like Elasticsearch. -Type and subtype are based on `span.type`, and `span.subtype`. - -[float] -[[service-maps-supported]] -=== Supported APM agents - -Service Maps are supported for the following APM agent versions: - -[horizontal] -Go agent:: ≥ v1.7.0 -iOS agent:: _Not yet supported_ -Java agent:: ≥ v1.13.0 -.NET agent:: ≥ v1.3.0 -Node.js agent:: ≥ v3.6.0 -PHP agent:: ≥ v1.2.0 -Python agent:: ≥ v5.5.0 -Ruby agent:: ≥ v3.6.0 -Real User Monitoring (RUM) agent:: ≥ v4.7.0 diff --git a/docs/apm/service-overview.asciidoc b/docs/apm/service-overview.asciidoc deleted file mode 100644 index a47ba33a7486f..0000000000000 --- a/docs/apm/service-overview.asciidoc +++ /dev/null @@ -1,193 +0,0 @@ -[role="xpack"] -[[service-overview]] -=== Service overview - -Selecting a non-mobile <<services,*service*>> brings you to the *Service overview*. -The *Service overview* contains a wide variety of charts and tables that provide -high-level visibility into how a service is performing across your infrastructure: - -* Service details like service version, runtime version, framework, and APM agent name and version -* Container and orchestration information -* Cloud provider, machine type, service name, region, and availability zone -* Serverless function names and event trigger type -* Latency, throughput, and errors over time -* Service dependencies - -[discrete] -[[service-time-comparison]] -=== Time series and expected bounds comparison - -For insight into the health of your services, you can compare how a service -performs relative to a previous time frame or to the expected bounds from the -corresponding {anomaly-job}. For example, has latency been slowly increasing -over time, did the service experience a sudden spike, is the throughput similar -to what the {ml} job expects – enabling a comparison can provide the answer. - -[role="screenshot"] -image::apm/images/time-series-expected-bounds-comparison.png[Time series and expected bounds comparison] - -Select the *Comparison* box to apply a time-based or expected bounds comparison. -The time-based comparison options are based on the selected time filter range: - -[options="header"] -|==== -|Time filter | Time comparison options - -|≤ 24 hours -|One day or one week - -|> 24 hours and ≤ 7 days -|One week - -|> 7 days -|An identical amount of time immediately before the selected time range -|==== - -You can use the expected bounds comparison if {ml-jobs} exist in your selected -environment and you have -{ml-docs}/setup.html#kib-visibility-spaces[access to the {ml-features}]. - -[discrete] -[[service-latency]] -=== Latency - -Response times for the service. You can filter the *Latency* chart to display the average, -95th, or 99th percentile latency times for the service. - -[role="screenshot"] -image::apm/images/latency.png[Service latency] - -[discrete] -[[service-throughput-transactions]] -=== Throughput and transactions - -// tag::throughput-transactions[] -The *Throughput* chart visualizes the average number of transactions per minute for the selected service. - -The *Transactions* table displays a list of _transaction groups_ for the -selected service and includes the latency, traffic, error rate, and the impact for each transaction. -Transactions that share the same name are grouped, and only one entry is displayed for each group. - -By default, transaction groups are sorted by _Impact_ to show the most used and slowest endpoints in your -service. If there is a particular endpoint you are interested in, click *View transactions* to view a -list of similar transactions on the <<transactions, transactions overview>> page. - -[role="screenshot"] -image::apm/images/traffic-transactions.png[Traffic and transactions] -// end::throughput-transactions[] - -[discrete] -[[service-error-rates]] -=== Failed transaction rate and errors - -// tag::ftr[] -The failed transaction rate represents the percentage of failed transactions from the perspective of the selected service. -It's useful for visualizing unexpected increases, decreases, or irregular patterns in a service's transactions. - -[TIP] -==== -HTTP **transactions** from the HTTP server perspective do not consider a `4xx` status code (client error) as a failure -because the failure was caused by the caller, not the HTTP server. Thus, `event.outcome=success` and there will be no increase in failed transaction rate. - -HTTP **spans** from the client perspective however, are considered failures if the HTTP status code is ≥ 400. -These spans will set `event.outcome=failure` and increase the failed transaction rate. - -If there is no HTTP status, both transactions and spans are considered successful unless an error is reported. -==== -// end::ftr[] - -The *Errors* table provides a high-level view of each error message when it first and last occurred, -along with the total number of occurrences. This makes it very easy to quickly see which errors affect -your services and take actions to rectify them. To do so, click *View errors*. - -[role="screenshot"] -image::apm/images/error-rate.png[failed transaction rate and errors] - -[discrete] -[[service-span-duration]] -=== Span types average duration and dependencies - -The *Time spent by span type* chart visualizes each span type's average duration and helps you determine -which spans could be slowing down transactions. The "app" label displayed under the -chart indicates that something was happening within the application. This could signal that the APM -agent does not have auto-instrumentation for whatever was happening during that time or that the time was spent in the -application code and not in database or external requests. - -// tag::dependencies[] -The *Dependencies* table displays a list of downstream services or external connections relevant -to the service at the selected time range. The table displays latency, throughput, failed transaction rate, and the impact of -each dependency. By default, dependencies are sorted by _Impact_ to show the most used and the slowest dependency. -If there is a particular dependency you are interested in, click *<<dependencies,View dependencies>>* to learn more about it. - -NOTE: Displaying dependencies for services instrumented with the Real User Monitoring (RUM) agent -requires an agent version ≥ v5.6.3. - -[role="screenshot"] -image::apm/images/spans-dependencies.png[Span type duration and dependencies] -// end::dependencies[] - -[discrete] -[[service-cold-start]] -=== Cold start rate - -The cold start rate chart is specific to serverless services, and displays the -percentage of requests that trigger a cold start of a serverless function. -A cold start occurs when a serverless function has not been used for a certain period of time. -Analyzing the cold start rate can be useful for deciding how much memory to allocate to a function, -or when to remove a large dependency. - -The cold start rate chart is currently supported for <<apm-lambda-cold-start-info,AWS Lambda>> -functions and Azure functions. - -[discrete] -[[service-instances]] -=== Instances - -The *Instances* table displays a list of all the available service instances within the selected time range. -Depending on how the service runs, the instance could be a host or a container. The table displays latency, throughput, -failed transaction, CPU usage, and memory usage for each instance. By default, instances are sorted by _Throughput_. - -[role="screenshot"] -image::apm/images/all-instances.png[All instances] - -[discrete] -[[service-metadata]] -=== Service metadata - -To view metadata relating to the service agent, and if relevant, the container and cloud provider, -click on each icon located at the top of the page beside the service name. - -[role="screenshot"] -image::apm/images/metadata-icons.png[Service metadata] - -*Service information* - -* Service version -* Runtime name and version -* Framework name -* APM agent name and version - -*Container information* - -* Operating system -* Containerized - Yes or no. -* Total number of instances -* Orchestration - -*Cloud provider information* - -* Cloud provider -* Cloud service name -* Availability zones -* Machine types -* Project ID -* Region - -*Serverless information* - -* Function name(s) -* Event trigger type - -*Alerts* - -* Recently fired alerts diff --git a/docs/apm/services.asciidoc b/docs/apm/services.asciidoc deleted file mode 100644 index c4deeb7e40322..0000000000000 --- a/docs/apm/services.asciidoc +++ /dev/null @@ -1,61 +0,0 @@ -[role="xpack"] -[[services]] -=== Services - -*Service* inventory provides a quick, high-level overview of the health and general -performance of all instrumented services. - -To help surface potential issues, services are sorted by their health status: -**critical** > **warning** > **healthy** > **unknown**. -Health status is powered by <<machine-learning-integration,machine learning>> -and requires anomaly detection to be enabled. - -In addition to health status, active alerts for each service are prominently displayed in the service inventory table. Selecting an active alert badge brings you to the <<apm-alerts,Alerts>> tab where you can learn more about the active alert and take action. - -[role="screenshot"] -image::apm/images/apm-services-overview.png[Example view of services table the APM app in Kibana] - -[float] -[[service-groups]] -==== Service groups - -beta::[] - -Group services together to build meaningful views that remove noise, simplify investigations across services, -and <<apm-alert-view-group,combine related alerts>>. -Service groups are {kib} space-specific and available for any users with appropriate access. - -// This screenshot is reused in the alerts docs -// Ensure it has an active alert showing -[role="screenshot"] -image::apm/images/apm-service-group.png[Example view of service group in the APM app in Kibana] - -To enable Service groups, open {kib} and navigate to **Stack Management** > **Advanced Settings** > **Observability**, -and enable the **Service groups feature**. - -To create a service group: - -. Navigate to **Observability** > **APM** > **Services**. -. Switch to **Service groups**. -. Click **Create group**. -. Specify a name, color, and description. -. Click **Select services**. -. Specify a <<kuery-query, Kibana Query Language (KQL)>> query to select services for the group. -Services that match the query within the last 24 hours will be assigned to the group. - -[NOTE] -==== -Once a service group has been saved, this list of services within it is static. -If a newly added service matches the KQL query, it will not be automatically added to the service group. -Similarly, if a service stops matching the KQL query, it will not be removed from the group. - -To update the list of services within a group, -edit the service group, click **Refresh** next to the KQL query, and click **Save group**. -==== - -**Examples** - -Not sure where to get started? Here are some sample queries you can build from: - -* Group services by environment--in this example, "production": `service.environment : "production"` -* Group services by name--this example groups those that end in "beat": `service.name : *beat` (matches services named "Auditbeat", "Heartbeat", "Filebeat", etc.) diff --git a/docs/apm/set-up.asciidoc b/docs/apm/set-up.asciidoc deleted file mode 100644 index c049fdd49ba6c..0000000000000 --- a/docs/apm/set-up.asciidoc +++ /dev/null @@ -1,16 +0,0 @@ -[role="xpack"] -[[apm-ui]] -== Set up the APM app - -++++ -<titleabbrev>Set up</titleabbrev> -++++ - -APM is available via the navigation sidebar in {Kib}. -If you have not already installed and configured Elastic APM, -follow the steps on the *Add data* page to get started. - -[role="screenshot"] -image::apm/images/apm-setup.png[Installation instructions on the APM page in Kibana] - -That's it! You're now ready to explore your data. diff --git a/docs/apm/settings.asciidoc b/docs/apm/settings.asciidoc deleted file mode 100644 index c11ab5f70f6dd..0000000000000 --- a/docs/apm/settings.asciidoc +++ /dev/null @@ -1,34 +0,0 @@ -// Do not link directly to this page. -// Link to the anchor in `/docs/settings/apm-settings.asciidoc` instead. -[role="xpack"] -[[apm-settings-in-kibana]] -== APM app settings - -++++ -<titleabbrev>Settings</titleabbrev> -++++ - -You do not need to configure any settings to use the APM app. It is enabled by default. - -[float] -[[apm-indices-settings]] -=== APM Indices - -include::./../settings/apm-settings.asciidoc[tag=apm-indices-settings] - -[float] -[[general-apm-settings]] -=== General APM settings - -include::./../settings/apm-settings.asciidoc[tag=general-apm-settings] - -[float] -[[apm-labs]] -=== APM Labs - -**APM Labs** allows you to easily try out new features that are technical preview. - -To enable APM labs, navigate to **APM** > **Settings** > **General settings** and toggle **Enable labs button in APM**. -Select **Save changes** and refresh the page. - -After enabling **APM Labs** select **Labs** in the toolbar to see the technical preview features available to try out. diff --git a/docs/apm/spans.asciidoc b/docs/apm/spans.asciidoc deleted file mode 100644 index 40af80cb864a9..0000000000000 --- a/docs/apm/spans.asciidoc +++ /dev/null @@ -1,73 +0,0 @@ -[role="xpack"] -[[spans]] -=== Trace sample timeline - -The trace sample timeline visualization is a bird's-eye view of what your application was doing while it was trying to respond to a request. -This makes it useful for visualizing where a selected transaction spent most of its time. - -[role="screenshot"] -image::apm/images/apm-transaction-sample.png[Example of distributed trace colors in the APM app in Kibana] - -View a span in detail by clicking on it in the timeline waterfall. -For example, when you click on an SQL Select database query, -the information displayed includes the actual SQL that was executed, how long it took, -and the percentage of the trace's total time. -You also get a stack trace, which shows the SQL query in your code. -Finally, APM knows which files are your code and which are just modules or libraries that you've installed. -These library frames will be minimized by default in order to show you the most relevant stack trace. - -TIP: A {apm-guide-ref}/data-model-spans.html[span] is the duration of a single event. -Spans are automatically captured by APM agents, and you can also define custom spans. -Each span has a type and is defined by a different color in the timeline/waterfall visualization. - -[role="screenshot"] -image::apm/images/apm-span-detail.png[Example view of a span detail in the APM app in Kibana] - -[float] -[[trace-sample-investigate]] -==== Investigate - -The trace sample timeline features an **Investigate** button which provides a quick way to jump -to other areas of the Elastic Observability UI while maintaining the context of the currently selected trace sample. -For example, quickly view: - -* logs and metrics for the selected pod -* logs and metrics for the selected host -* trace logs for the selected `trace.id` -* uptime status of the selected domain -* the <<service-maps,service map>> filtered by the selected trace -* the selected transaction in <<discover,Discover>> -* your <<custom-links,custom links>> - -[float] -[[distributed-tracing]] -==== Distributed tracing - -When a trace travels through multiple services it is known as a _distributed trace_. -In APM, the colors in a distributed trace represent different services and -are listed in the order they occur. - -[role="screenshot"] -image::apm/images/apm-services-trace.png[Example of distributed trace colors in the APM app in Kibana] - -As application architectures are shifting from monolithic to more distributed, service-based architectures, -distributed tracing has become a crucial feature of modern application performance monitoring. -It allows you to trace requests through your service architecture automatically, and visualize those traces in one single view in the APM app. -From initial web requests to your front-end service, to queries made to your back-end services, -this makes finding possible bottlenecks throughout your application much easier and faster. - -[role="screenshot"] -image::apm/images/apm-distributed-tracing.png[Example view of the distributed tracing in APM app in Kibana] - -Don't forget; by definition, a distributed trace includes more than one transaction. -When viewing distributed traces in the timeline waterfall, -you'll see this icon: image:apm/images/transaction-icon.png[APM icon], -which indicates the next transaction in the trace. -For easier problem isolation, transactions can be collapsed in the waterfall by clicking -the icon to the left of the transactions. -Transactions can also be expanded and viewed in detail by clicking on them. - -After exploring these traces, -you can return to the full trace by clicking *View full trace*. - -TIP: Distributed tracing is supported by all APM agents, and there's no additional configuration needed. diff --git a/docs/apm/storage-explorer.asciidoc b/docs/apm/storage-explorer.asciidoc deleted file mode 100644 index 7ab7b0ec70c45..0000000000000 --- a/docs/apm/storage-explorer.asciidoc +++ /dev/null @@ -1,91 +0,0 @@ -[[storage-explorer]] -=== Storage Explorer - -Analyze your APM data and manage costs with **storage explorer**. -For example, analyze the storage footprint of each of your services to see which are producing -large amounts of data--then change the sample rate of a service to lower the amount of data ingested. -Or, expand the time filter to visualize data trends over time so that you can better forecast -and prepare for future storage needs. - -[role="screenshot"] -image::apm/images/storage-explorer-overview.png[APM Storage Explorer] - -[float] -==== Index lifecycle phases - -A default {apm-guide-ref}/ilm-how-to.html[index lifecycle policy] is applied to each APM data stream, -but can be customized depending on your business needs. -Use the **Index lifecycle phase** dropdown to visualize and analyze your storage by phase. - -Customizing the default APM index lifecycle policies can save money by specifying things like: - -* The point at which an index can be moved to less performant hardware. -* The point at which availability is not as critical and the number of replicas can be reduced. -* When the index can be safely deleted. - -See {apm-guide-ref}/ilm-how-to.html[Index lifecycle management] to learn more about customizing -the default APM index lifecycle policies. - -[float] -==== Service size chart - -The service size chart displays the estimated size of each service over time. -Expand the time filter to visualize data trends and estimate daily data generation. - -[float] -==== Service statistics table - -The service statistics table provides detailed information on each service: - -* A list of **service environments**. -* The **sampling rate**. This value is calculated by dividing the number of sampled transactions by total throughput. -It might differ from the configured sampling rate for two reasons: with head-based sampling, -the initial service makes the sampling decision, and with tail-based sampling, -granular policies allow you to set multiple sample rates. -* The estimated **size on disk**. This storage size includes both primary and replica shards and is -calculated by prorating the total size of your indices by the service's document count divided by -the total number of documents. -* Number of **transactions**, **spans**, **errors**, and **metrics** — doc count and size on disk. - -[role="screenshot"] -image::apm/images/storage-explorer-expanded.png[APM Storage Explorer service breakdown] - -As you explore your service statistics, you might want to take action to reduce the number of -documents and therefore storage size of a particular service. - -[float] -===== Reduce the number of transactions -To reduce the number of transactions a service generates, configure a more aggressive -{apm-guide-ref}/sampling.html[transaction sampling policy]. Transaction sampling lowers -the amount of data ingested without negatively impacting the usefulness of your data. - -[float] -===== Reduce the number of spans -To reduce the number of spans a service generates, enable -{apm-guide-ref}/span-compression.html[span compression]. Span compression saves on data -and transfer costs by compressing multiple, similar spans into a single span. - -[float] -===== Reduce the number of metrics -To reduce the number of system, runtime, and application metrics, -tune the APM agent or agents that are collecting the data. -You can disable the collection of specific metrics with the **disable metrics** configuration. -Or, you can set the **metrics interval** to zero seconds to deactivate metrics entirely. -Most APM agents support both options. -See the relevant {apm-agents-ref}[APM agent configuration options] for more details. - -[float] -===== Reduce the number of errors -To reduce the number of errors a service generate, -work with your developers to change how exceptions are handled in your code. - -[float] -==== Privileges - -Storage Explorer requires expanded privileges to view. -See <<apm-app-storage-explorer-user-create>> for more information. - -[float] -==== Limitations - -Multi-cluster deployments are not supported. diff --git a/docs/apm/tab-widgets/apm-app-reader/content.asciidoc b/docs/apm/tab-widgets/apm-app-reader/content.asciidoc deleted file mode 100644 index 6b9c996035f6c..0000000000000 --- a/docs/apm/tab-widgets/apm-app-reader/content.asciidoc +++ /dev/null @@ -1,45 +0,0 @@ -// tag::classic-indices[] -[options="header"] -|==== -|Type |Privilege |Purpose - -|Index -|`read` on `apm-*` -|Read-only access to `apm-*` data - -|Index -|`view_index_metadata` on `apm-*` -|Read-only access to `apm-*` index metadata -|==== -// end::classic-indices[] - -// tag::data-streams[] -[options="header"] -|==== -|Type |Privilege |Purpose - -|Index -|`read` on `logs-apm*` -|Read-only access to `logs-apm*` data - -|Index -|`view_index_metadata` on `logs-apm*` -|Read-only access to `logs-apm*` index metadata - -|Index -|`read` on `metrics-apm*` -|Read-only access to `metrics-apm*` data - -|Index -|`view_index_metadata` on `metrics-apm*` -|Read-only access to `metrics-apm*` index metadata - -|Index -|`read` on `traces-apm*` -|Read-only access to `traces-apm*` data - -|Index -|`view_index_metadata` on `traces-apm*` -|Read-only access to `traces-apm*` index metadata -|==== -// end::data-streams[] diff --git a/docs/apm/tab-widgets/apm-app-reader/widget.asciidoc b/docs/apm/tab-widgets/apm-app-reader/widget.asciidoc deleted file mode 100644 index a325b04f6cd86..0000000000000 --- a/docs/apm/tab-widgets/apm-app-reader/widget.asciidoc +++ /dev/null @@ -1,40 +0,0 @@ -++++ -<div class="tabs" data-tab-group="apm-app-reader"> - <div role="tablist" aria-label="APM app reader"> - <button role="tab" - aria-selected="true" - aria-controls="data-streams-tab" - id="data-streams"> - Data streams - </button> - <button role="tab" - aria-selected="false" - aria-controls="classic-indices-tab" - id="classic-indices" - tabindex="-1"> - Classic APM indices - </button> - </div> - <div tabindex="0" - role="tabpanel" - id="data-streams-tab" - aria-labelledby="data-streams"> -++++ - -include::content.asciidoc[tag=data-streams] - -++++ - </div> - <div tabindex="0" - role="tabpanel" - id="classic-indices-tab" - aria-labelledby="classic-indices" - hidden=""> -++++ - -include::content.asciidoc[tag=classic-indices] - -++++ - </div> -</div> -++++ \ No newline at end of file diff --git a/docs/apm/tab-widgets/central-config-users/content.asciidoc b/docs/apm/tab-widgets/central-config-users/content.asciidoc deleted file mode 100644 index 0945050d9a861..0000000000000 --- a/docs/apm/tab-widgets/central-config-users/content.asciidoc +++ /dev/null @@ -1,53 +0,0 @@ -// tag::classic-indices[] -[options="header"] -|==== -|Type |Privilege |Purpose - -|Index -|`read` on `apm-*` -|Read-only access to `apm-*` data - -|Index -|`view_index_metadata` on `apm-*` -|Read-only access to `apm-*` index metadata -|==== -// end::classic-indices[] - -// tag::data-streams[] -[options="header"] -|==== -|Type |Privilege |Purpose - -|Index -|`read` on `apm-agent-configuration` -|Read-only access to `apm-agent-configuration` data - -|Index -|`view_index_metadata` on `apm-agent-configuration` -|Read-only access to `apm-agent-configuration` index metadata - -|Index -|`read` on `logs-apm*` -|Read-only access to `logs-apm*` data - -|Index -|`view_index_metadata` on `logs-apm*` -|Read-only access to `logs-apm*` index metadata - -|Index -|`read` on `metrics-apm*` -|Read-only access to `metrics-apm*` data - -|Index -|`view_index_metadata` on `metrics-apm*` -|Read-only access to `metrics-apm*` index metadata - -|Index -|`read` on `traces-apm*` -|Read-only access to `traces-apm*` data - -|Index -|`view_index_metadata` on `traces-apm*` -|Read-only access to `traces-apm*` index metadata -|==== -// end::data-streams[] diff --git a/docs/apm/tab-widgets/central-config-users/widget.asciidoc b/docs/apm/tab-widgets/central-config-users/widget.asciidoc deleted file mode 100644 index 586a3515a718d..0000000000000 --- a/docs/apm/tab-widgets/central-config-users/widget.asciidoc +++ /dev/null @@ -1,40 +0,0 @@ -++++ -<div class="tabs" data-tab-group="central-config-manager"> - <div role="tablist" aria-label="Central config manager"> - <button role="tab" - aria-selected="true" - aria-controls="data-streams-tab" - id="data-streams"> - Data streams - </button> - <button role="tab" - aria-selected="false" - aria-controls="classic-indices-tab" - id="classic-indices" - tabindex="-1"> - Classic APM indices - </button> - </div> - <div tabindex="0" - role="tabpanel" - id="data-streams-tab" - aria-labelledby="data-streams"> -++++ - -include::content.asciidoc[tag=data-streams] - -++++ - </div> - <div tabindex="0" - role="tabpanel" - id="classic-indices-tab" - aria-labelledby="classic-indices" - hidden=""> -++++ - -include::content.asciidoc[tag=classic-indices] - -++++ - </div> -</div> -++++ \ No newline at end of file diff --git a/docs/apm/tab-widgets/storage-explorer-user/content.asciidoc b/docs/apm/tab-widgets/storage-explorer-user/content.asciidoc deleted file mode 100644 index 8661be8d3b6e4..0000000000000 --- a/docs/apm/tab-widgets/storage-explorer-user/content.asciidoc +++ /dev/null @@ -1,37 +0,0 @@ -// tag::classic-indices[] -[options="header"] -|==== -|Type |Privilege |Purpose - -|Cluster -|`monitor` -|Monitor the disk space used by APM indices - -|Index -|`monitor` on `apm-*` -|Monitor access to `apm-*` for storage explorer -|==== -// end::classic-indices[] - -// tag::data-streams[] -[options="header"] -|==== -|Type |Privilege |Purpose - -|Cluster -|`monitor` -|Monitor the disk space used by APM data streams - -|Index -|`monitor` on `logs-apm*` -|Monitor access to `logs-apm*` for storage explorer - -|Index -|`monitor` on `metrics-apm*` -|Monitor access to `metrics-apm*` for storage explorer - -|Index -|`monitor` on `traces-apm*` -|Monitor access to `traces-apm*` for storage explorer -|==== -// end::data-streams[] diff --git a/docs/apm/tab-widgets/storage-explorer-user/widget.asciidoc b/docs/apm/tab-widgets/storage-explorer-user/widget.asciidoc deleted file mode 100644 index a577f2e8b94ae..0000000000000 --- a/docs/apm/tab-widgets/storage-explorer-user/widget.asciidoc +++ /dev/null @@ -1,40 +0,0 @@ -++++ -<div class="tabs" data-tab-group="apm-app-storage-explorer-reader"> - <div role="tablist" aria-label="APM app storage explorer-reader"> - <button role="tab" - aria-selected="true" - aria-controls="data-streams-tab" - id="data-streams"> - Data streams - </button> - <button role="tab" - aria-selected="false" - aria-controls="classic-indices-tab" - id="classic-indices" - tabindex="-1"> - Classic APM indices - </button> - </div> - <div tabindex="0" - role="tabpanel" - id="data-streams-tab" - aria-labelledby="data-streams"> -++++ - -include::content.asciidoc[tag=data-streams] - -++++ - </div> - <div tabindex="0" - role="tabpanel" - id="classic-indices-tab" - aria-labelledby="classic-indices" - hidden=""> -++++ - -include::content.asciidoc[tag=classic-indices] - -++++ - </div> -</div> -++++ \ No newline at end of file diff --git a/docs/apm/traces.asciidoc b/docs/apm/traces.asciidoc deleted file mode 100644 index 4c912a03dcf64..0000000000000 --- a/docs/apm/traces.asciidoc +++ /dev/null @@ -1,40 +0,0 @@ -[role="xpack"] -[[traces]] -=== Traces - -TIP: Traces link together related transactions to show an end-to-end performance of how a request was served -and which services were part of it. -In addition to the Traces overview, you can view your application traces in the <<spans,trace sample timeline waterfall>>. - -*Traces* displays your application's entry (root) transactions. -Transactions with the same name are grouped together and only shown once in this table. -If you're using <<distributed-tracing,distributed tracing>>, -this view is key to finding the critical paths within your application. - -By default, transactions are sorted by _Impact_. -Impact helps show the most used and slowest endpoints in your service -- in other words, -it's the collective amount of pain a specific endpoint is causing your users. -If there's a particular endpoint you're worried about, select it to view its -<<transaction-details,transaction details>>. - -You can also use queries to filter and search the transactions shown on this page. -Note that only properties available on root transactions are searchable. -For example, you can't search for `label.tier: 'high'`, as that field is only available on non-root transactions. - -[role="screenshot"] -image::apm/images/apm-traces.png[Example view of the Traces overview in APM app in Kibana] - -[float] -[[trace-explorer]] -==== Trace explorer - -preview::[] - -**Trace explorer** is an experimental top-level search tool that allows you to query your traces using <<kuery-query,Kibana Query Language (KQL)>> or {ref}/eql.html[Event Query Language (EQL)]. - -Curate your own custom queries, or use the <<service-maps,**Service Map**>> to find and select edges to automatically generate queries based on your selection: - -[role="screenshot"] -image::apm/images/trace-explorer.png[Trace explorer] - -Enable **Trace explorer** in <<apm-labs,APM Labs>> or in <<observability-apm-trace-explorer-tab,{kib} advanced settings>>. diff --git a/docs/apm/transactions.asciidoc b/docs/apm/transactions.asciidoc deleted file mode 100644 index 284d28c7b60c0..0000000000000 --- a/docs/apm/transactions.asciidoc +++ /dev/null @@ -1,177 +0,0 @@ -[role="xpack"] -[[transactions]] -=== Transactions - -TIP: A {apm-guide-ref}/data-model-transactions.html[transaction] describes an event captured by an Elastic APM agent instrumenting a service. -APM agents automatically collect performance metrics on HTTP requests, database queries, and much more. - -[role="screenshot"] -image::apm/images/apm-transactions-overview.png[Example view of transactions table in the APM app in Kibana] - -The *Latency*, *Throughput*, *Failed transaction rate*, *Time spent by span type*, and *Cold start rate* -charts display information on all transactions associated with the selected service: - -*Latency*:: -Response times for the service. Options include average, 95th, and 99th percentile. -If there's a weird spike that you'd like to investigate, -you can simply zoom in on the graph - this will adjust the specific time range, -and all of the data on the page will update accordingly. - -*Throughput*:: -Visualize response codes: `2xx`, `3xx`, `4xx`, etc. -Useful for determining if more responses than usual are being served with a particular response code. -Like in the latency graph, you can zoom in on anomalies to further investigate them. - -[[transaction-error-rate]] -*Failed transaction rate*:: -The failed transaction rate represents the percentage of failed transactions from the perspective of the selected service. -It's useful for visualizing unexpected increases, decreases, or irregular patterns in a service's transactions. -+ -[TIP] -==== -HTTP **transactions** from the HTTP server perspective do not consider a `4xx` status code (client error) as a failure -because the failure was caused by the caller, not the HTTP server. Thus, `event.outcome=success` and there will be no increase in failed transaction rate. - -HTTP **spans** from the client perspective however, are considered failures if the HTTP status code is ≥ 400. -These spans will set `event.outcome=failure` and increase the failed transaction rate. - -If there is no HTTP status, both transactions and spans are considered successful unless an error is reported. -==== - -*Time spent by span type*:: -Visualize where your application is spending most of its time. -For example, is your app spending time in external calls, database processing, or application code execution? -+ -The time a transaction took to complete is also recorded and displayed on the chart under the "app" label. -"app" indicates that something was happening within the application, but we're not sure exactly what. -This could be a sign that the APM agent does not have auto-instrumentation for whatever was happening during that time. -+ -It's important to note that if you have asynchronous spans, the sum of all span times may exceed the duration of the transaction. - -*Cold start rate*:: -Only applicable to serverless transactions, this chart displays the percentage of requests that trigger a cold start of a serverless function. -See <<apm-lambda-cold-start-info>> for more information. - -[discrete] -[[transactions-table]] -=== Transactions table - -The *Transactions* table displays a list of _transaction groups_ for the selected service. -In other words, this view groups all transactions of the same name together, -and only displays one entry for each group. - -[role="screenshot"] -image::apm/images/apm-transactions-table.png[Example view of the transactions table in the APM app in Kibana] - -By default, transaction groups are sorted by _Impact_. -Impact helps show the most used and slowest endpoints in your service - in other words, -it's the collective amount of pain a specific endpoint is causing your users. -If there's a particular endpoint you're worried about, you can click on it to view the <<transaction-details, transaction details>>. - -[IMPORTANT] -==== -If you only see one route in the Transactions table, or if you have transactions named "unknown route", -it could be a symptom that the APM agent either wasn't installed correctly or doesn't support your framework. - -For further details, including troubleshooting and custom implementation instructions, -refer to the documentation for each {apm-agents-ref}[APM Agent] you've implemented. -==== - -[discrete] -[[rum-transaction-overview]] -=== RUM Transaction overview - -The transaction overview page is customized for the JavaScript RUM agent. -Specifically, the page highlights *page load times* for your service: - -[role="screenshot"] -image::apm/images/apm-geo-ui.png[average page load duration distribution] - -Additional RUM goodies, like core vitals, and visitor breakdown by browser, location, and device, -are available in the Observability User Experience tab. -// To do -// Add link to the Observability UE docs when complete - -[discrete] -[[transaction-details]] -=== Transaction details - -Selecting a transaction group will bring you to the *transaction* details. -This page is visually similar to the transaction overview, but it shows data from all transactions within -the selected transaction group. - -[role="screenshot"] -image::apm/images/apm-transactions-overview.png[Example view of response time distribution] - -[[transaction-duration-distribution]] -==== Latency distribution - -The latency distribution shows a plot of all transaction durations for the given time period. -The following screenshot shows a typical distribution -and indicates most of our requests were served quickly -- awesome! -The requests on the right are taking longer than average; we probably need to focus on them. - -[role="screenshot"] -image::apm/images/apm-transaction-duration-dist.png[Example view of latency distribution graph] - -Click and drag to select a latency duration _bucket_ to display up to 500 trace samples. - -[[transaction-trace-sample]] -==== Trace samples - -Trace samples are based on the _bucket_ selection in the *Latency distribution* chart; -update the samples by selecting a new _bucket_. -The number of requests per bucket is displayed when hovering over the graph, -and the selected bucket is highlighted to stand out. - -Each bucket presents up to ten trace samples in a *timeline*, trace sample *metadata*, -and any related *logs*. - -*Trace sample timeline* - -Each sample has a trace timeline waterfall that shows how a typical request in that bucket executed. -This waterfall is useful for understanding the parent/child hierarchy of transactions and spans, -and ultimately determining _why_ a request was slow. -For large waterfalls, expand problematic transactions and collapse well-performing ones -for easier problem isolation and troubleshooting. - -[role="screenshot"] -image::apm/images/apm-transaction-sample.png[Example view of transactions sample] - -NOTE: More information on timeline waterfalls is available in <<spans, spans>>. - -*Trace sample metadata* - -Learn more about a trace sample in the *Metadata* tab: - -* Labels - Custom labels added by APM agents -* HTTP request/response information -* Host information -* Container information -* Service - The service/application runtime, APM agent, name, etc.. -* Process - The process id that served up the request. -* APM agent information -* URL -* User - Requires additional configuration, but allows you to see which user experienced the current transaction. -* FaaS information, like cold start, AWS request ID, trigger type, and trigger request ID - -TIP: All of this data is stored in documents in Elasticsearch. -This means you can select "Actions - View transaction in Discover" to see the actual Elasticsearch document under the discover tab. - -*Trace sample logs* - -The *Logs* tab displays logs related to the sampled trace. - -include::./logs.asciidoc[tag=log-overview] - -[role="screenshot"] -image::apm/images/apm-logs-tab.png[APM logs tab] - -[[transaction-latency-correlations]] -==== Correlations - -Correlations surface attributes of your data that are potentially correlated with high-latency or erroneous transactions. -To learn more, see <<correlations>>. - -[role="screenshot"] -image::apm/images/correlations-hover.png[APM lattency correlations] diff --git a/docs/apm/troubleshooting.asciidoc b/docs/apm/troubleshooting.asciidoc deleted file mode 100644 index 93464d23518b6..0000000000000 --- a/docs/apm/troubleshooting.asciidoc +++ /dev/null @@ -1,191 +0,0 @@ -[[troubleshooting]] -== Troubleshooting - -++++ -<titleabbrev>Troubleshooting</titleabbrev> -++++ - -This section describes common problems you might encounter with the APM app. -To add to this page, please consider opening a -https://github.com/elastic/kibana/pulls[pull request] with your proposed changes. - -If your issue is potentially related to other components of the APM ecosystem, -don't forget to check our other troubleshooting guides or discussion forum: - -* {apm-guide-ref}/troubleshoot-apm.html[APM Server troubleshooting] -* {apm-dotnet-ref}/troubleshooting.html[.NET agent troubleshooting] -* {apm-go-ref}/troubleshooting.html[Go agent troubleshooting] -* {apm-ios-ref}/troubleshooting.html[iOS agent troubleshooting] -* {apm-java-ref}/trouble-shooting.html[Java agent troubleshooting] -* {apm-node-ref}/troubleshooting.html[Node.js agent troubleshooting] -* {apm-php-ref}/troubleshooting.html[PHP agent troubleshooting] -* {apm-py-ref}/troubleshooting.html[Python agent troubleshooting] -* {apm-ruby-ref}/debugging.html[Ruby agent troubleshooting] -* {apm-rum-ref}/troubleshooting.html[RUM troubleshooting] -* https://discuss.elastic.co/c/apm[APM discussion forum]. - -[discrete] -[[troubleshooting-apm-app]] -== Troubleshoot common APM app problems - -* <<no-apm-data-found>> -* <<troubleshooting-too-many-transactions>> -* <<troubleshooting-unknown-route>> -* <<troubleshooting-fields-unsearchable>> -* <<service-map-rum-connections>> - -[float] -[[no-apm-data-found]] -=== No APM data found - -This section can help with any of the following: - -* Data isn't displaying in the APM app -* Data isn't displaying in the APM app after an upgrade -* You see a message like "No Services Found", -* You see errors like "Fielddata is disabled on text fields by default..." - -These problems are likely to be caused by a missing index template or ingest pipeline. -By default, {fleet} sets up these and other APM assets when the APM integration is installed. -Try reinstalling the APM integration by navigating to -**Integrations** > **Elastic APM** > **Manage in Fleet** > **Settings** > **Reinstall Elastic APM**. - -Because assets cannot be applied to indices retroactively, -after reinstalling the APM integration you must either wait for the index to rollover or force a rollover. -To force a rollover, use the {ref}/indices-rollover-index.html[rollover API] to target the relevant {apm-guide-ref}/apm-data-streams.html[APM data streams]. - -[float] -[[troubleshooting-too-many-transactions]] -=== Too many unique transaction names - -Transaction names are defined in each APM agent; when an APM agent supports a framework, -it includes logic for naming the transactions that the framework creates. -In some cases though, like when using an APM agent's API to create custom transactions, -it is up to the user to define a pattern for transaction naming. -When transactions are named incorrectly, each unique URL can be associated with a unique transaction group—causing -an explosion in the number of transaction groups per service, and leading to inaccuracies in the APM app. - -To fix a large number of unique transaction names, -you need to change how you are using the APM agent API to name your transactions. -To do this, ensure you are **not** naming based on parameters that can change. -For example, user ids, product ids, order numbers, query parameters, etc., -should be stripped away, and commonality should be found between your unique URLs. - -Let's look at an example from the RUM agent documentation. Here are a few URLs you might find on Elastic.co: - -[source,yml] ----- -// Blog Posts -https://www.elastic.co/blog/reflections-on-three-years-in-the-elastic-public-sector -https://www.elastic.co/blog/say-heya-to-the-elastic-search-awards -https://www.elastic.co/blog/and-the-winner-of-the-elasticon-2018-training-subscription-drawing-is - -// Documentation -https://www.elastic.co/guide/en/elastic-stack/current/index.html -https://www.elastic.co/guide/en/apm/get-started/current/index.html -https://www.elastic.co/guide/en/infrastructure/guide/current/index.html ----- - -These URLs, like most, include unique names. -If we named transactions based on each unique URL, we'd end up with the problem described above—a -very large number of different transaction names. -Instead, we should strip away the unique information and group our transactions based on common information. -In this case, that means naming all blog transactions, `/blog`, and all documentation transactions, `/guide`. - -If you feel like you'd be losing valuable information by following this naming convention, don't fret! -You can always add additional metadata to your transactions using {apm-guide-ref}/data-model-metadata.html#data-model-labels[labels] (indexed) or -{apm-guide-ref}/data-model-metadata.html#data-model-custom[custom context] (non-indexed). - -After ensuring you've correctly named your transactions, -you might still see errors in the APM app related to transaction group limit reached: - -`The number of transaction groups has been reached. Current APM server capacity for handling unique transaction groups has been reached. There are at least X transactions missing in this list. Please decrease the number of transaction groups in your service or increase the memory allocated to APM server.` - -You will see this warning if an agent is creating too many transaction groups. This could indicate incorrect instrumentation which will have to be fixed in your application. Alternatively you can increase the memory of the APM server. - -`Number of transaction groups exceed the allowed maximum(1,000) that are displayed. The maximum number of transaction groups displayed in Kibana has been reached. Try narrowing down results by using the query bar..` - -You will see this warning if your results have more than `1000` unique transaction groups. Alternatively you can use the query bar to reduce the number of unique transaction groups in your results. - -**More information** - -While this can happen with any APM agent, it typically occurs with the RUM agent. -For more information on how to correctly set `transaction.name` in the RUM agent, -see {apm-rum-ref}/custom-transaction-name.html[custom initial page load transaction names]. - -The RUM agent can also set the `transaction.name` when observing for transaction events. -See {apm-rum-ref}/agent-api.html#observe[`apm.observe()`] for more information. - -If your problem is occurring in a different APM agent, the tips above still apply. -See the relevant {apm-agents-ref}[Agent API documentation] to adjust how you're naming your transactions. - -[float] -[[troubleshooting-unknown-route]] -=== Unknown route - -The {apm-app-ref}/transactions.html[transaction overview] will only display helpful information -when the transactions in your services are named correctly. -If you're seeing "GET unknown route" or "unknown route" in the APM app, -it could be a sign that something isn't working as it should. - -Elastic APM agents come with built-in support for popular frameworks out-of-the-box. -This means, among other things, that the APM agent will try to automatically name HTTP requests. -As an example, the Node.js agent uses the route that handled the request, while the Java agent uses the Servlet name. - -"Unknown route" indicates that the APM agent can't determine what to name the request, -perhaps because the technology you're using isn't supported, the agent has been installed incorrectly, -or because something is happening to the request that the agent doesn't understand. - -To resolve this, you'll need to head over to the relevant {apm-agents-ref}[APM agent documentation]. -Specifically, view the agent's supported technologies page. -You can also use the agent's public API to manually set a name for the transaction. - -[float] -[[troubleshooting-fields-unsearchable]] -=== Fields are not searchable - -In Elasticsearch, index templates are used to define settings and mappings that determine how fields should be analyzed. -The recommended index templates for APM are installed by {fleet} when the Elastic APM integration is installed. -These templates, by default, enable and disable indexing on certain fields. - -As an example, some APM agents store cookie values in `http.request.cookies`. -Since `http.request` has disabled dynamic indexing, and `http.request.cookies` is not declared in a custom mapping, -the values in `http.request.cookies` are not indexed and thus not searchable. - -*Ensure an APM data view exists* -As a first step, you should ensure the correct data view exists. -In {kib}, go to *Stack Management* > *Data views*. -You should see the APM data view--the default is -`traces-apm*,apm-*,logs-apm*,apm-*,metrics-apm*,apm-*`. -If you don't, the data view doesn't exist. -To fix this, navigate to the APM app in {kib} and select *Add data*. -In the APM tutorial, click *Load Kibana objects* to create the APM data view. - -If creating an APM data view doesn't solve the problem, -see <<no-apm-data-found>> for further troubleshooting. - -*Ensure a field is searchable* -There are two things you can do to if you'd like to ensure a field is searchable: - -1. Index your additional data as {apm-guide-ref}/data-model-metadata.html[labels] instead. -These are dynamic by default, which means they will be indexed and become searchable and aggregatable. - -2. Create a custom mapping for the field. -// link will be added in a later PR. -// docs will be added in https://github.com/elastic/apm-server/pull/6940 - -[float] -[[service-map-rum-connections]] -=== Service Maps: no connection between client and server - -If the service map is not showing an expected connection between the client and server, -it's likely because you haven't configured -{apm-rum-ref}/distributed-tracing-guide.html[`distributedTracingOrigins`]. - - -This setting is necessary, for example, for cross-origin requests. -If you have a basic web application that provides data via an API on `localhost:4000`, -and serves HTML from `localhost:4001`, you'd need to set `distributedTracingOrigins: ['https://localhost:4000']` -to ensure the origin is monitored as a part of distributed tracing. -In other words, `distributedTracingOrigins` is consulted prior to the APM agent adding the -distributed tracing `traceparent` header to each request. diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 72eee773793c6..e51f77d096adf 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -470,10 +470,6 @@ The plugin exposes the static DefaultEditorController class to consume. |WARNING: Missing README. -|{kib-repo}blob/{branch}/x-pack/plugins/observability_solution/assets_data_access[assetsDataAccess] -|WARNING: Missing README. - - |{kib-repo}blob/{branch}/x-pack/plugins/banners/README.md[banners] |Allow to add a header banner that will be displayed on every page of the Kibana application @@ -570,6 +566,10 @@ security and spaces filtering. |This plugin provides Kibana user interfaces for managing the Enterprise Search solution and its products, App Search and Workplace Search. +|{kib-repo}blob/{branch}/x-pack/plugins/observability_solution/entities_data_access/README.md[entitiesDataAccess] +|Exposes services to access entities data. + + |{kib-repo}blob/{branch}/x-pack/plugins/observability_solution/entity_manager/README.md[entityManager] |This plugin provides access to observed asset data, such as information about hosts, pods, containers, services, and more. diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 73789c750e015..4a88d33c83e5d 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -187,7 +187,7 @@ this setting stores part of the URL in your browser session to keep the URL short. [[theme-darkmode]]`theme:darkMode`:: -The UI theme that the {kib} UI should use. +The UI theme that the {kib} UI should use. Set to `enabled` or `disabled` to enable or disable the dark theme. Set to `system` to have the {kib} UI theme follow the system theme. You must refresh the page to apply the setting. @@ -418,7 +418,7 @@ beta:[] Enables the usage of service transaction metrics, which are low cardinal [[observability-apm-labs]]`observability:apmLabsButton`:: Enable or disable the APM Labs button -- a quick way to enable and disable technical preview features in APM. -See <<apm-labs>> to learn more. +// See <<apm-labs>> to learn more. [[observability-apm-critical-path]]`observability:apmEnableCriticalPath`:: When enabled, displays the critical path of a trace. @@ -453,7 +453,7 @@ preview:[] Display Amazon Lambda metrics in the service metrics tab. Shows the Uptime app even if there is no recent Heartbeat data. [[observability-apm-enable-comparison]]`observability:enableComparisonByDefault`:: -Determines whether the <<service-time-comparison, comparison feature>> is enabled or disabled by default in the APM app. +Determines whether the comparison feature is enabled or disabled by default in the APM app. [[observability-apm-enable-infra-view]]`observability:enableInfrastructureView`:: Enables the Infrastructure view in the APM app. diff --git a/docs/observability/index.asciidoc b/docs/observability/index.asciidoc index c924cea3712dd..78ca03296df8f 100644 --- a/docs/observability/index.asciidoc +++ b/docs/observability/index.asciidoc @@ -4,7 +4,7 @@ = Observability Observability enables you to add and monitor your logs, system -metrics, uptime data, and application traces, as a single stack. +metrics, uptime data, and application traces, as a single stack. With *Observability*, you have: @@ -44,8 +44,8 @@ image::observability/images/logs-app.png[Logs app in {kib}] The {metrics-app} in {kib} enables you to visualize infrastructure metrics to help diagnose problematic spikes, identify high resource utilization, -automatically discover and track pods, and unify your metrics -with logs and APM data in {es}. +automatically discover and track pods, and unify your metrics +with logs and APM data in {es}. To get started with the {metrics-app}, see {observability-guide}/ingest-metrics.html[Ingest metrics]. @@ -75,7 +75,7 @@ The APM app in {kib} enables you to monitors software services and applications collect unhandled errors and exceptions, and automatically pick up basic host-level metrics and agent specific metrics. -To get started with the APM app, see <<apm-ui,Set up the APM app>>. +// To get started with the APM app, see <<apm-ui,Set up the APM app>>. [role="screenshot"] image::observability/images/apm-app.png[APM app in {kib}] diff --git a/docs/redirects.asciidoc b/docs/redirects.asciidoc index 767037d39536d..06a2fc7886b58 100644 --- a/docs/redirects.asciidoc +++ b/docs/redirects.asciidoc @@ -111,10 +111,10 @@ This page was deleted. See <<development-visualize-index>>. deprecated::[7.9.0] -Watcher error reports have been removed and replaced with Kibana's <<apm-alerts,alerting and actions>> feature. -To create error alerts with new tool, select **Alerts** - **Create threshold alert** - **Error rate**. +// Watcher error reports have been removed and replaced with Kibana's <<apm-alerts,alerting and actions>> feature. +// To create error alerts with new tool, select **Alerts** - **Create threshold alert** - **Error rate**. -More information on this new feature is available in <<apm-alerts>>. +// More information on this new feature is available in <<apm-alerts>>. [role="exclude",id="development-security-rbac"] == Role-based access control @@ -437,4 +437,331 @@ For the most up-to-date API details, refer to the [role="exclude",id="add-case-connectors"] == Add connectors to cases -This content has moved. Refer to <<manage-cases-settings>>. \ No newline at end of file +This content has moved. Refer to <<manage-cases-settings>>. + +//// +APM redirects +//// + +:apm-docs-move-notice: This documentation has moved to the {observability-guide}/apm.html[Observability guide] as of version 8.14. + +[role="exclude",id="xpack-apm"] +== APM + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-ui.html[the APM UI documentation]. + +[role="exclude",id="apm-ui"] +== Set up + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-ui.html[the APM UI documentation]. + +[role="exclude",id="apm-getting-started"] +== Get started + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm.html[the APM UI documentation]. + +[role="exclude",id="services"] +== Services + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-services.html[Services]. + +[role="exclude",id="traces"] +== Traces + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-traces.html[Traces]. + +[role="exclude",id="dependencies"] +== Dependencies + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-dependencies.html[Dependencies]. + +[role="exclude",id="service-maps"] +== Service Map + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-service-maps.html[Service Map]. + +[float] +[[service-maps-supported]] +=== Supported APM agents + +Refer to {observability-guide}/apm-service-maps.html#service-maps-supported[Supported APM agents]. + +[role="exclude",id="service-overview"] +== Service overview + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-service-overview.html[Service overview]. + +[float] +[[service-span-duration]] +=== Span types average duration and dependencies + +Refer to {observability-guide}/apm-service-overview.html#service-span-duration[Service overview]. + +[role="exclude",id="mobile-service-overview"] +== Mobile service overview + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-mobile-service-overview.html[Mobile service overview]. + +[role="exclude",id="transactions"] +== Transactions + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-transactions.html[Transactions]. + +[role="exclude",id="spans"] +== Trace sample timeline + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-spans.html[Trace sample timeline]. + +[role="exclude",id="errors"] +== Errors + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-errors.html[Errors]. + +[role="exclude",id="metrics"] +== Metrics + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-metrics.html[Metrics]. + +[role="exclude",id="infrastructure"] +== Infrastructure + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-infrastructure.html[Infrastructure]. + +[role="exclude",id="logs"] +== Logs + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-logs.html[Logs]. + +[role="exclude",id="apm-how-to"] +== How-to guides + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-how-to-guides.html[How-to guides]. + +[role="exclude",id="agent-configuration"] +== Configure APM agents with central config + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-agent-configuration.html[Configure APM agents with central config]. + +[role="exclude",id="apm-spaces"] +== Control access to APM data + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-spaces.html[Control access to APM data]. + +[role="exclude",id="apm-alerts"] +== Create an alert + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-alerts.html[Create an alert]. + +[role="exclude",id="custom-links"] +== Create custom links + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-custom-links.html[Create custom links]. + +[role="exclude",id="filters"] +== Filter data + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-filters.html[Filter data]. + +[float] +[[environment-selector]] +=== Service environment filter + +Refer to {observability-guide}/apm-filters.html#environment-selector[Filter data]. + +[role="exclude",id="correlations"] +== Find transaction latency and failure correlations + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-correlations.html[Find transaction latency and failure correlations]. + +[role="exclude",id="agent-explorer"] +== Identify deployment details for APM agents + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-agent-explorer.html[Identify deployment details for APM agents]. + +[role="exclude",id="machine-learning-integration"] +== Integrate with machine learning + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-machine-learning-integration.html[Integrate with machine learning]. + +[role="exclude",id="mobile-session-explorer"] +== Exploring mobile sessions with Discover + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-mobile-session-explorer.html[Exploring mobile sessions with Discover]. + +[role="exclude",id="_viewing_sessions_with_discover"] +== Viewing sessions with Discover + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-mobile-session-explorer.html#viewing-sessions-with-discover[Viewing sessions with Discover]. + +[role="exclude",id="apm-lambda"] +== Observe Lambda functions + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-lambda.html[Observe Lambda functions]. + +[role="exclude",id="advanced-queries"] +== Query your data + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-advanced-queries.html[Query your data]. + +[role="exclude",id="storage-explorer"] +== Storage Explorer + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-storage-explorer.html[Storage Explorer]. + +[role="exclude",id="transactions-annotations"] +== Track deployments with annotations + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-transactions-annotations.html[Track deployments with annotations]. + +[role="exclude",id="apm-app-users"] +== Users and privileges + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-app-users.html[Users and privileges]. + +[role="exclude",id="apm-app-reader"] +== Create an APM reader user + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-app-reader.html[Create an APM reader user]. + +[role="exclude",id="apm-app-annotation-user-create"] +== Create an annotation user + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-app-annotation-user-create.html[Create an annotation user]. + +[role="exclude",id="apm-app-central-config-user"] +== Create a central config user + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-app-central-config-user.html[Create a central config user]. + +[role="exclude",id="apm-app-storage-explorer-user-create"] +== Create a storage explorer user + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-app-storage-explorer-user-create.html[Create a storage explorer user]. + +[role="exclude",id="apm-app-api-user"] +== Create an API user + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-app-api-user.html[Create an API user]. + +[role="exclude",id="apm-settings-in-kibana"] +== Settings + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-settings-in-kibana.html[Settings]. + +[role="exclude",id="apm-api"] +== REST API + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-api.html[REST API]. + +[role="exclude",id="agent-config-api"] +== Agent Configuration API + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-agent-config-api.html[Agent Configuration API]. + +[role="exclude",id="apm-annotation-api"] +== Annotation API + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-annotation-api.html[Annotation API]. + +[role="exclude",id="rum-sourcemap-api"] +== RUM source map API + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-rum-sourcemap-api.html[RUM source map API]. + +[role="exclude",id="agent-key-api"] +== APM agent Key API + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-agent-key-api.html[APM agent Key API]. + +[role="exclude",id="troubleshooting"] +== Troubleshooting + +{apm-docs-move-notice} + +Refer to {observability-guide}/apm-app-troubleshooting.html[Troubleshooting]. + +:!apm-docs-move-notice: diff --git a/docs/settings/apm-settings.asciidoc b/docs/settings/apm-settings.asciidoc index 05d8cc35f1cde..3cd04a4ff3733 100644 --- a/docs/settings/apm-settings.asciidoc +++ b/docs/settings/apm-settings.asciidoc @@ -24,7 +24,7 @@ Index settings in the APM app take precedence over those set in `kibana.yml`. Starting in version 8.2.0, APM indices are {kib} Spaces-aware; Changes to APM index settings will only apply to the currently enabled space. -See <<apm-spaces>> for more information. +// See <<apm-spaces>> for more information. [role="screenshot"] image::settings/images/apm-settings.png[APM app settings in Kibana] diff --git a/docs/settings/reporting-settings.asciidoc b/docs/settings/reporting-settings.asciidoc index 87ee0b9bac099..49aa22de9fd35 100644 --- a/docs/settings/reporting-settings.asciidoc +++ b/docs/settings/reporting-settings.asciidoc @@ -12,17 +12,16 @@ You can configure `xpack.reporting` settings in your `kibana.yml` to: -* <<general-reporting-settings,Enable the {report-features}>> -* <<encryption-keys,Configure the encryption key>> -* <<reporting-job-queue-settings,Manage background jobs>> -* <<reporting-capture-settings,Capture screenshots>> -* <<reporting-network-policy,Restrict requests with a Reporting network policy>> -* <<reporting-csv-settings,Increase the byte limit for CSV exports>> -* <<reporting-kibana-server-settings,Control how the {report-features} communicate with the {kib} server>> +* <<general-reporting-settings,Enable or disable the {report-features}>> +* <<encryption-keys,Configure an encryption key to protect sensitive authentication data>> +* <<reporting-advanced-settings,Choose an access control model of how users will be granted privileges to {report-features}>> +* <<reporting-job-queue-settings,Manage the way reporting tasks run in the {kib} server background>> +* <<reporting-capture-settings,Control how screenshots are captured for PNG/PDF reports>> +* <<reporting-csv-settings,Control the limits and capabilities of CSV reports>> [float] [[general-reporting-settings]] -==== Enable reporting +=== Enable reporting [[xpack-enable-reporting]]`xpack.reporting.enabled` {ess-icon}:: When `true`, enables the {report-features}. Set this to `false` to disable {report-features} entirely. The default is `true`. @@ -38,7 +37,7 @@ If needed, you can also prevent a {kib} instance from claiming reporting work by [float] [[encryption-keys]] -==== Encryption key setting +=== Encryption key setting By default, an encryption key is generated for the {report-features} each time you start {kib}. If a static encryption key is not persisted in @@ -58,13 +57,41 @@ The static encryption key for reporting. Use an alphanumeric text string that is xpack.reporting.encryptionKey: "something_secret" -------------------------------------------------------------------------------- +[float] +[[reporting-advanced-settings]] +=== Security settings + +Reporting has two forms of access control: each user can only access their own reports, +and custom roles determine who has the privilege to generate reports. When Reporting is configured with +<<kibana-privileges, {kib} application privileges>>, you can control the spaces and applications where users +are allowed to generate reports. + +[NOTE] +============================================================================ +The `xpack.reporting.roles` settings are for a deprecated system of access control in Reporting. Turning off +this feature allows API keys to generate reports, and allows reporting access through {kib} application +privileges. We recommend that you explicitly turn off reporting's deprecated access control feature by adding +`xpack.reporting.roles.enabled: false` to kibana.yml. This will enable you to create custom roles that provide +application privileges for reporting, as described in <<grant-user-access, granting users access to +reporting>>. +============================================================================ + +[[xpack-reporting-roles-enabled]] `xpack.reporting.roles.enabled`:: +deprecated:[7.14.0,The default for this setting will be `false` in an upcoming version of {kib}.] Sets access +control to a set of assigned reporting roles, specified by `xpack.reporting.roles.allow`. Defaults to `true`. + +`xpack.reporting.roles.allow`:: +deprecated:[7.14.0] In addition to superusers, specifies the roles that can generate reports using the +{ref}/security-api.html#security-role-apis[{es} role management APIs]. Requires `xpack.reporting.roles.enabled` +to be `true`. Defaults to `[ "reporting_user" ]`. + [float] [[reporting-job-queue-settings]] -==== Background job settings +=== Background job settings -Reporting generates reports in the background and jobs are coordinated using documents -in {es}. Depending on how often you generate reports and the overall number of -reports, you might need to change the following settings. +Reporting generates reports on the {kib} server as background tasks, and jobs are coordinated using documents +in {es}. Depending on how often you generate reports and the overall number of reports, you might need to +change the following settings. `xpack.reporting.capture.maxAttempts` {ess-icon}:: If capturing a report fails for any reason, {kib} will re-queue the report job for retry, as many times as this setting. Defaults to `3`. @@ -85,18 +112,25 @@ reporting requires identical values for <<xpack-reporting-encryptionKey, `xpack. security is enabled, <<xpack-security-encryptionKey, `xpack.security.encryptionKey`>>. `xpack.reporting.queue.pollInterval`:: -Specifies the {time-units}[time] that the reporting poller waits between polling the index for any pending Reporting jobs. Can be specified as number of milliseconds. Defaults to `3s`. +Specifies the {time-units}[time] that the reporting poller waits between polling the index for any pending Reporting jobs. Can be specified as a number of milliseconds. Defaults to `3s`. [[xpack-reporting-q-timeout]] `xpack.reporting.queue.timeout` {ess-icon}:: {time-units}[How long] each worker has to produce a report. If your machine is slow or under heavy load, you might need to increase this timeout. If a Reporting job execution goes over this time limit, the job is marked -as a failure and no download will be available. Can be specified as number of milliseconds. Defaults to `4m`. +as a failure and no download will be available. Can be specified as a number of milliseconds. Defaults to `4m`. [float] [[reporting-capture-settings]] -==== Capture settings +=== PNG/PDF settings -Reporting uses an internal "screenshotting" plugin to capture screenshots from {kib}. The following settings control the capturing process. +[NOTE] +============ +include::../user/reporting/reporting-pdf-limitations.asciidoc[] +============ + +To generate PDF and PNG files, Reporting uses an internal "screenshotting" plugin which manages a headless browser that captures screenshots from {kib}. + +The following settings control the capturing process. `xpack.screenshotting.capture.timeouts.openUrl` {ess-icon}:: Specify the {time-units}[time] to allow the Reporting browser to wait for the "Loading..." screen to dismiss @@ -123,7 +157,7 @@ deprecated:[8.0.0,This setting has no effect.] Specify the {time-units}[amount o [float] [[reporting-chromium-settings]] -==== Chromium settings +==== Chromium headless browser settings For PDF and PNG reports, Reporting spawns a headless Chromium browser process on the server to load and capture a screenshot of the {kib} app. When installing {kib} on Linux and Windows platforms, the Chromium binary comes bundled with the {kib} download. For Mac platforms, the Chromium binary is downloaded the first time {kib} is started. @@ -139,13 +173,36 @@ The uri for the proxy server. Providing the username and password for the proxy `xpack.screenshotting.browser.chromium.proxy.bypass`:: An array of hosts that should not go through the proxy server and should use a direct connection instead. Examples of valid entries are "elastic.co", "*.elastic.co", ".elastic.co", ".elastic.co:5601". +[float] +[[reporting-kibana-server-settings]] +==== {kib} server settings for headless browser connection + +To generate screenshots for PNG and PDF reports, Reporting opens the {kib} web interface using a local +connection to the server. In most cases, using a local connection to the {kib} server presents no issue. If +you prefer the headless browser to connect to {kib} using a specific hostname, there are a number of +settings that allow the headless browser to connect to {kib} through a proxy, rather than directly. + +[NOTE] +============ +The `xpack.reporting.kibanaServer` settings are optional. Take caution when editing these settings. Adding +these settings can cause the PDF/PNG {report-features} to fail. If reports fail, inspect the server logs and +pay attention to errors regarding the headless browser being unable to connect to the server. The full {kib} +URL that Reporting is attempting to open is logged during report execution. +============ + +`xpack.reporting.kibanaServer.port`:: The port for accessing {kib}. + +`xpack.reporting.kibanaServer.protocol`:: The protocol for accessing {kib}, typically `http` or `https`. + +[[xpack-kibanaServer-hostname]] `xpack.reporting.kibanaServer.hostname`:: The hostname for accessing {kib}. + [float] [[reporting-network-policy]] -=== Network policy settings +==== Network policy settings for headless Chromium restrictions -To generate PDF reports, *Reporting* uses the Chromium browser to fully load the {kib} page on the server. This potentially involves sending requests to external hosts. For example, a request might go to an external image server to show a field formatted as an image, or to show an image in a Markdown visualization. +To generate PDF reports, Reporting uses a headless Chromium browser to fully load the {kib} page on the server. This potentially involves sending requests to external hosts. For example, a request might go to an external image server to show a field formatted as an image, or to show an image in a Markdown visualization. -If the Chromium browser is asked to send a request that violates the network policy, *Reporting* stops processing the page before the request goes out, and the report is marked as a failure. Additional information about the event is in the {kib} server logs. +If the headless Chromium browser is asked to send a request that violates the network policy, it will stop processing the page before the request goes out, and the report is marked as a failure. Additional information about the event is in the {kib} server logs. NOTE: {kib} installations are not designed to be publicly accessible over the internet. The Reporting network policy and other capabilities of the Elastic Stack security features do not change this condition. @@ -153,7 +210,7 @@ NOTE: {kib} installations are not designed to be publicly accessible over the in Capturing a screenshot from a {kib} page involves sending out requests for all the linked web assets. For example, a Markdown visualization can show an image from a remote server. `xpack.screenshotting.networkPolicy.enabled`:: -When `false`, disables the *Reporting* network policy. Defaults to `true`. +When `false`, disables the headless browser network policy. Defaults to `true`. `xpack.screenshotting.networkPolicy.rules`:: A policy is specified as an array of objects that describe what to allow or deny based on a host or protocol. If a host or protocol is not specified, the rule matches any host or protocol. @@ -202,30 +259,17 @@ The `file:` protocol is always denied, even if no network policy is configured. [float] [[reporting-csv-settings]] -==== CSV settings - -[[xpack-reporting-csv]] `xpack.reporting.csv.maxSizeBytes` {ess-icon}:: -The maximum {byte-units}[byte size] of a CSV file before being truncated. This setting exists to prevent large -exports from causing performance and storage issues. Can be specified as number of bytes. Defaults to `250mb`. +=== CSV settings [NOTE] ============ -We recommend using CSV reports to export moderate amounts of data only. The feature enables analysis of data in -external tools, but it's not intended for bulk export or to backup {es} data. If you need to export more than -250 MB of CSV, rather than increasing `xpack.reporting.csv.maxSizeBytes`, please use filters to create multiple -smaller reports, or extract the data you need directly from {es}. - -The following deployment configurations may lead to failed report jobs or incomplete reports: - -* Any shard needed for search is unavailable. -* Data is stored on slow storage tiers. -* Network latency between nodes is high. -* {ccs-cap} is used. - -To export large amounts of data we recommend using {es} APIs directly. See {ref}/point-in-time-api.html[Point -in time API], or {ref}/sql-rest-format.html#_csv[SQL with CSV response data format]. +include::../user/reporting/reporting-csv-limitations.asciidoc[] ============ +[[xpack-reporting-csv]] `xpack.reporting.csv.maxSizeBytes` {ess-icon}:: +The maximum {byte-units}[byte size] of a CSV file before being truncated. This setting exists to prevent large +exports from causing performance and storage issues. Can be specified as a number of bytes. Defaults to `250mb`. + `xpack.reporting.csv.scroll.size`:: Number of documents retrieved from {es} for each scroll iteration during a CSV export. Defaults to `500`. [NOTE] @@ -235,13 +279,15 @@ You may need to lower this setting if the default number of documents creates a `xpack.reporting.csv.scroll.duration`:: Amount of {time-units}[time] allowed before {kib} cleans the scroll context during a CSV export. Valid option is either `auto` or {time-units}[time], Defaults to `30s`. + [NOTE] ============ -If search latency in {es} is sufficiently high, such as if you are using {ccs}, you may either need to increase the time setting or set this config value to `auto`. When the config value is set to `auto` the scroll context will be preserved for as long as is possible, before the report task is terminated due to the limits of `xpack.reporting.queue.timeout`. +If search latency in {es} is sufficiently high, such as if you are using {ccs}, you may either need to increase the time setting or set this config value to `auto`. When the config value is set to `auto` the scroll context will be preserved for as long as possible, before the report task is terminated due to the limits of `xpack.reporting.queue.timeout`. ============ `xpack.reporting.csv.scroll.strategy`:: Choose the API method used to page through data during CSV export. Valid options are `scroll` and `pit`. Defaults to `pit`. + [NOTE] ============ Each method has its own unique limitations which are important to understand. @@ -264,43 +310,3 @@ dashboard and later download them in *Stack Management > Reporting*. Defaults to `xpack.reporting.csv.useByteOrderMarkEncoding`:: Adds a byte order mark (`\ufeff`) at the beginning of the CSV file. Defaults to `false`. - -[float] -[[reporting-advanced-settings]] -==== Security settings - -With Security enabled, Reporting has two forms of access control: each user can only access their own reports, and custom roles determine who has privilege to generate reports. When Reporting is configured with <<kibana-privileges, {kib} application privileges>>, you can control the spaces and applications where users are allowed to generate reports. - -[NOTE] -============================================================================ -The `xpack.reporting.roles` settings are for a deprecated system of access control in Reporting. Turning off this feature allows API Keys to generate reports, and allows reporting access through {kib} application privileges. We recommend you explicitly turn off reporting's deprecated access control feature by adding `xpack.reporting.roles.enabled: false` in kibana.yml. This will enable you to create custom roles that provide application privileges for reporting, as described in <<grant-user-access, granting users access to reporting>>. -============================================================================ - -[[xpack-reporting-roles-enabled]] `xpack.reporting.roles.enabled`:: -deprecated:[7.14.0,The default for this setting will be `false` in an upcoming version of {kib}.] Sets access control to a set of assigned reporting roles, specified by `xpack.reporting.roles.allow`. Defaults to `true`. - -`xpack.reporting.roles.allow`:: -deprecated:[7.14.0] In addition to superusers, specifies the roles that can generate reports using the {ref}/security-api.html#security-role-apis[{es} role management APIs]. Requires `xpack.reporting.roles.enabled` to be `true`. Defaults to `[ "reporting_user" ]`. - -[float] -[[reporting-kibana-server-settings]] -==== {kib} server settings - -To generate screenshots for PNG and PDF reports, Reporting opens the {kib} web interface using a local -connection on the server. In most cases, using a local connection to the {kib} server presents no issue. If -you prefer the headless browser to connect to {kib} using a specific hostname, there are a number of -settings that allow the headless browser to connect to {kib} through a proxy, rather than directly. - -[NOTE] -============ -The `xpack.reporting.kibanaServer` settings are optional. Take caution when editing these settings. Adding -these settings can cause the {report-features} to fail. If report fail, -inspect the server logs. The full {kib} URL that Reporting is attempting to - open is logged during report execution. -============ - -`xpack.reporting.kibanaServer.port`:: The port for accessing {kib}.port`>> value. - -`xpack.reporting.kibanaServer.protocol`:: The protocol for accessing {kib}, typically `http` or `https`. - -[[xpack-kibanaServer-hostname]] `xpack.reporting.kibanaServer.hostname`:: The hostname for accessing {kib}. diff --git a/docs/siem/index.asciidoc b/docs/siem/index.asciidoc index 6afddb1dd5fbe..06d9097dcc5dc 100644 --- a/docs/siem/index.asciidoc +++ b/docs/siem/index.asciidoc @@ -6,159 +6,5 @@ <titleabbrev>Security</titleabbrev> ++++ -https://www.elastic.co/security[Elastic Security] combines SIEM threat detection features with endpoint -prevention and response capabilities in one solution. These analytical and -protection capabilities, leveraged by the speed and extensibility of -Elasticsearch, enable analysts to defend their organization from threats before -damage and loss occur. +Elastic Security combines threat detection analytics, cloud native security, and endpoint protection capabilities in a single solution, so you can quickly detect, investigate, and respond to threats and vulnerabilities across your environment. To learn more about how {elastic-sec} works and what it offers, refer to the {security-guide}/es-overview.html[documentation]. -Elastic Security provides the following security benefits and capabilities: - -* A detection engine to identify attacks and system misconfigurations -* A workspace for event triage and investigations -* Interactive visualizations to investigate process relationships -* Inbuilt case management with automated actions -* Detection of signatureless attacks with prebuilt machine learning anomaly jobs -and detection rules - -[discrete] -== Elastic Security components and workflow - -The following diagram provides a comprehensive illustration of the Elastic Security workflow. - -[role="screenshot"] -image::images/workflow.png[] - -Here's an overview of the flow and its components: - -* Data is shipped from your hosts to {es} via beat modules and the Elastic https://www.elastic.co/endpoint-security/[Endpoint Security agent integration]. This integration provides capabilities such as collecting events, detecting and preventing {security-guide}/detection-engine-overview.html#malware-prevention[malicious activity], and artifact delivery. The {fleet-guide}/fleet-overview.html[{fleet}] app is used to -install and manage agents and integrations on your hosts. -+ -The Endpoint Security integration ships the following data sets: -+ -*** *Windows*: Process, network, file, DNS, registry, DLL and driver loads, -malware security detections -*** *Linux/macOS*: Process, network, file -+ -* https://www.elastic.co/integrations?solution=security[Beat modules]: {beats} -are lightweight data shippers. Beat modules provide a way of collecting and -parsing specific data sets from common sources, such as cloud and OS events, -logs, and metrics. Common security-related modules are listed {security-guide}/ingest-data.html#enable-beat-modules[here]. -* The {security-app} in {kib} is used to manage the *Detection engine*, -*Cases*, and *Timeline*, as well as administer hosts running Endpoint Security: -** Detection engine: Automatically searches for suspicious host and network -activity via the following: -*** {security-guide}/detection-engine-overview.html#detection-engine-overview[Detection rules]: Periodically search the data -({es} indices) sent from your hosts for suspicious events. When a suspicious -event is discovered, a detection alert is generated. External systems, such as -Slack and email, can be used to send notifications when alerts are generated. -You can create your own rules and make use of our {security-guide}/prebuilt-rules.html[prebuilt ones]. -*** {security-guide}/detections-ui-exceptions.html[Exceptions]: Reduce noise and the number of -false positives. Exceptions are associated with rules and prevent alerts when -an exception's conditions are met. *Value lists* contain source event -values that can be used as part of an exception's conditions. When -Elastic {endpoint-sec} is installed on your hosts, you can add malware exceptions -directly to the endpoint from the Security app. -*** {security-guide}/machine-learning.html#included-jobs[{ml-cap} jobs]: Automatic anomaly detection of host and -network events. Anomaly scores are provided per host and can be used with -detection rules. -** {security-guide}/timelines-ui.html[Timeline]: Workspace for investigating alerts and events. -Timelines use queries and filters to drill down into events related to -a specific incident. Timeline templates are attached to rules and use predefined -queries when alerts are investigated. Timelines can be saved and shared with -others, as well as attached to Cases. -** {security-guide}/cases-overview.html[Cases]: An internal system for opening, tracking, and sharing -security issues directly in the Security app. Cases can be integrated with -external ticketing systems. -** {security-guide}/admin-page-ov.html[Administration]: View and manage hosts running {endpoint-sec}. - -{security-guide}/ingest-data.html[Ingest data to Elastic Security] and {security-guide}/install-endpoint.html[Configure and install the Elastic Endpoint integration] describe how to ship security-related -data to {es}. - - -For more background information, see: - -* https://www.elastic.co/products/elasticsearch[{es}]: A real-time, -distributed storage, search, and analytics engine. {es} excels at indexing -streams of semi-structured data, such as logs or metrics. -* https://www.elastic.co/products/kibana[{kib}]: An open-source analytics and -visualization platform designed to work with {es}. You use {kib} to search, -view, and interact with data stored in {es} indices. You can easily compile -advanced data analysis and visualize your data in a variety of charts, tables, -and maps. - -[discrete] -=== Compatibility with cold tier nodes - -Cold tier is a {ref}/data-tiers.html[data tier] that holds time-series data that is accessed only occasionally. In {stack} version >=7.11.0, {elastic-sec} supports cold tier data for the following {es} indices: - -* Index patterns specified in `securitySolution:defaultIndex` -* Index patterns specified in the definitions of detection rules, except for indicator match rules -* Index patterns specified in the data sources selector on various {security-app} pages - -{elastic-sec} does NOT support cold tier data for the following {es} indices: - -* Index patterns controlled by {elastic-sec}, including signals and list indices -* Index patterns specified in indicator match rules - -Using cold tier data for unsupported indices may result in detection rule timeouts and overall performance degradation. - -[discrete] -[[self-protection]] -==== Elastic Endpoint self-protection - -Self-protection means that {elastic-endpoint} has guards against users and attackers that may try to interfere with its functionality. This protection feature is consistently enhanced to prevent attackers who may attempt to use newer, more sophisticated tactics to interfere with the {elastic-endpoint}. Self-protection is enabled by default when {elastic-endpoint} installs on supported platforms, listed below. - -Self-protection is enabled on the following 64-bit Windows versions: - -* Windows 8.1 -* Windows 10 -* Windows Server 2012 R2 -* Windows Server 2016 -* Windows Server 2019 - -And on the following macOS versions: - -* macOS 10.15 (Catalina) -* macOS 11 (Big Sur) - -NOTE: Other Windows and macOS variants (and all Linux distributions) do not have self-protection. - -For {stack} version >= 7.11.0, self-protection defines the following permissions: - -* Users -- even Administrator/root -- *cannot* delete {elastic-endpoint} files (located at `c:\Program Files\Elastic\Endpoint` on Windows, and `/Library/Elastic/Endpoint` on macOS). -* Users *cannot* terminate the {elastic-endpoint} program or service. -* Administrator/root users *can* read the endpoint's files. On Windows, the easiest way to read Endpoint files is to start an Administrator `cmd.exe` prompt. On macOS, an Administrator can use the `sudo` command. -* Administrator/root users *can* stop the {elastic-agent}'s service. On Windows, run the `sc stop "Elastic Agent"` command. On macOS, run the `sudo launchctl stop elastic-agent` command. - - -[discrete] -[[siem-integration]] -=== Integration with other Elastic products - -You can use {elastic-sec} with other Elastic products and features to help you -identify and investigate suspicious activity: - -* https://www.elastic.co/products/stack/machine-learning[{ml-cap}] -* https://www.elastic.co/products/stack/alerting[Alerting] -* https://www.elastic.co/products/stack/canvas[Canvas] - -[discrete] -[[data-sources]] -=== APM transaction data sources - -By default, {elastic-sec} monitors {apm-app-ref}/apm-getting-started.html[APM] -`apm-*-transaction*` indices. To add additional APM indices, update the -index patterns in the `securitySolution:defaultIndex` setting ({kib} -> Stack Management -> Advanced Settings -> `securitySolution:defaultIndex`). - -[discrete] -[[ecs-compliant-reqs]] -=== ECS compliance data requirements - -The {ecs-ref}[Elastic Common Schema (ECS)] defines a common set of fields used for -storing event data in Elasticsearch. ECS helps users normalize their event data -to better analyze, visualize, and correlate the data represented in their -events. {elastic-sec} supports events and indicator index data from any ECS-compliant data source. - -IMPORTANT: {elastic-sec} requires {ecs-ref}[ECS-compliant data]. If you use third-party data collectors to ship data to {es}, the data must be mapped to ECS. -{security-guide}/siem-field-reference.html[Elastic Security ECS field reference] lists ECS fields used in {elastic-sec}. diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index 545155e656893..83ea01ee3edf5 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -13,7 +13,7 @@ To make sure you can access alerting and actions, see the <<alerting-prerequisit ============================================== Alerting works by running checks on a schedule to detect conditions defined by a rule. When a condition is met, the rule tracks it as an _alert_ and responds by triggering one or more _actions_. -Actions typically involve interaction with {kib} services or third party integrations. _Connectors_ enable actions to talk to these services and integrations. +Actions typically involve interaction with {kib} services or third party integrations. _Connectors_ enable actions to talk to these services and integrations. This section describes all of these elements and how they operate together. [float] @@ -21,7 +21,7 @@ This section describes all of these elements and how they operate together. A rule specifies a background task that runs on the {kib} server to check for specific conditions. {kib} provides two types of rules: stack rules that are built into {kib} and the rules that are registered by {kib} apps. For more information, refer to <<rule-types>>. -A rule consists of three main parts: +A rule consists of three main parts: * _Conditions_: what needs to be detected? * _Schedule_: when/how often should detection checks run? @@ -41,7 +41,7 @@ The following sections describe each part of the rule in more detail. [[alerting-concepts-conditions]] === Conditions -Under the hood, {kib} rules detect conditions by running a JavaScript function on the {kib} server, which gives it the flexibility to support a wide range of conditions, anything from the results of a simple {es} query to heavy computations involving data from multiple sources or external systems. +Under the hood, {kib} rules detect conditions by running a JavaScript function on the {kib} server, which gives it the flexibility to support a wide range of conditions, anything from the results of a simple {es} query to heavy computations involving data from multiple sources or external systems. These conditions are packaged and exposed as _rule types_. A rule type hides the underlying details of the condition, and exposes a set of parameters to control the details of the conditions to detect. @@ -80,7 +80,7 @@ The _action frequency_ defines when the action runs (for example, only when the Some types of rules enable you to further refine the conditions under which actions run. For example, you can specify that actions run only when an alert occurs within a specific time frame or when it matches a KQL query. -Each action definition is therefore a template: all the parameters needed to invoke a service are supplied except for specific values that are only known at the time the rule condition is detected. +Each action definition is therefore a template: all the parameters needed to invoke a service are supplied except for specific values that are only known at the time the rule condition is detected. In the server monitoring example, the `email` connector type is used, and `server` is mapped to the body of the email, using the template string `CPU on {{server}} is high`. @@ -103,7 +103,7 @@ A rule consists of conditions, actions, and a schedule. When conditions are met, image::images/rule-concepts-summary.svg[Rules, connectors, alerts and actions work together to convert detection into action] -. Any time a rule's conditions are met, an alert is created. This example checks for servers with average CPU > 0.9. Three servers meet the condition, so three alerts are created. +. Any time a rule's conditions are met, an alert is created. This example checks for servers with average CPU > 0.9. Three servers meet the condition, so three alerts are created. . Alerts create actions according to the action frequency, as long as they are not muted or throttled. When actions are created, its properties are filled with actual values. In this example, three actions are created when the threshold is met, and the template string {{server}} is replaced with the appropriate server name for each alert. . {kib} runs the actions, sending notifications by using a third party integration like an email service. . If the third party integration has connection parameters or credentials, {kib} fetches these from the appropriate connector. @@ -119,14 +119,14 @@ independent alerting systems. This section will clarify some of the important differences in the function and intent of the two systems. -Functionally, the {alert-features} differ in that: +Functionally, the {alert-features} differ in that: * Scheduled checks are run on {kib} instead of {es} * {kib} <<alerting-concepts-conditions,rules hide the details of detecting conditions>> through rule types, whereas watches provide low-level control over inputs, conditions, and transformations. * {kib} rules track and persist the state of each detected condition through alerts. This makes it possible to mute and throttle individual alerts, and detect changes in state such as resolution. * Actions are linked to alerts. Actions are fired for each occurrence of a detected condition, rather than for the entire rule. -At a higher level, the {alert-features} allow rich integrations across use cases like <<xpack-apm,*APM*>>, <<metrics-app,*Metrics*>>, <<xpack-siem,*Security*>>, and <<uptime-app,*Uptime*>>. +At a higher level, the {alert-features} allow rich integrations across use cases like <<apm-app,*APM*>>, <<metrics-app,*Metrics*>>, <<xpack-siem,*Security*>>, and <<uptime-app,*Uptime*>>. Prepackaged rule types simplify setup and hide the details of complex, domain-specific detections, while providing a consistent interface across {kib}. -- diff --git a/docs/user/alerting/create-and-manage-rules.asciidoc b/docs/user/alerting/create-and-manage-rules.asciidoc index 5a17a583fb387..1caae10ead421 100644 --- a/docs/user/alerting/create-and-manage-rules.asciidoc +++ b/docs/user/alerting/create-and-manage-rules.asciidoc @@ -34,7 +34,7 @@ more information, go to <<alerting-security>>. === Create and edit rules Some rules must be created within the context of a {kib} app like -<<metrics-app,Metrics>>, <<xpack-apm,APM>>, or <<uptime-app,Uptime>>, but others +<<metrics-app,Metrics>>, <<apm-app,*APM*>>, or <<uptime-app,Uptime>>, but others are generic. Generic rule types can be created in *{rules-ui}* by clicking the *Create rule* button. This will launch a flyout that guides you through selecting a rule type and configuring its conditions and actions. diff --git a/docs/user/index.asciidoc b/docs/user/index.asciidoc index 419574804312c..5ef23d7a3b718 100644 --- a/docs/user/index.asciidoc +++ b/docs/user/index.asciidoc @@ -30,8 +30,6 @@ include::{kibana-root}/docs/observability/index.asciidoc[] include::{kibana-root}/docs/playground/index.asciidoc[] -include::{kibana-root}/docs/apm/index.asciidoc[] - include::{kibana-root}/docs/siem/index.asciidoc[] include::dev-tools.asciidoc[] @@ -49,4 +47,3 @@ include::api.asciidoc[] include::plugins.asciidoc[] include::troubleshooting/index.asciidoc[] - diff --git a/docs/user/reporting/index.asciidoc b/docs/user/reporting/index.asciidoc index 5075caad71813..03352865e616c 100644 --- a/docs/user/reporting/index.asciidoc +++ b/docs/user/reporting/index.asciidoc @@ -73,10 +73,16 @@ to the {es} {ref}/index-lifecycle-management.html[{ilm-init} documentation]. [float] [[csv-limitations]] -=== CSV reports limitations +=== CSV report limitations include::reporting-csv-limitations.asciidoc[] +[float] +[[pdf-limitations]] +=== PNG/PDF report limitations + +include::reporting-pdf-limitations.asciidoc[] + [float] [[share-a-direct-link]] == Share direct links diff --git a/docs/user/reporting/reporting-csv-troubleshooting.asciidoc b/docs/user/reporting/reporting-csv-troubleshooting.asciidoc index a0eb782ce3d3b..f0edec18bd8e5 100644 --- a/docs/user/reporting/reporting-csv-troubleshooting.asciidoc +++ b/docs/user/reporting/reporting-csv-troubleshooting.asciidoc @@ -4,33 +4,20 @@ <titleabbrev>CSV</titleabbrev> ++++ -The CSV export feature in Kibana makes queries to Elasticsearch and formats the results into CSV. -This feature offers a solution that attempts to provide the most benefit to the most use cases. -However, things could go wrong during export. -Elasticsearch can stop responding, repeated querying can take so long that authentication tokens can time -out, and the format of exported data can be too complex for spreadsheet applications to handle. -Such situations are outside of the control of Kibana. -If the use case becomes complex enough, it's recommended that you create scripts that query Elasticsearch directly, using a scripting language like Python and the public {es} APIs. - -For more advice about common problems, refer to <<reporting-troubleshooting>>. - [NOTE] ============ -It is recommended that you use CSV reports to export moderate amounts of data only. -The feature enables analysis of data in external tools, but it's not intended for bulk export or to backup {es} data. -If you need to export more than 250 MB of CSV, rather than increasing <<reporting-csv-settings,`xpack.reporting.csv.maxSizeBytes`>>, use -filters to create multiple smaller reports or extract the data you need directly from {es}. - -The following deployment configurations may lead to failed report jobs or incomplete reports: +include::reporting-csv-limitations.asciidoc[] +============ -* Any shard needed for search is unavailable. -* Data is stored on slow storage tiers. -* Network latency between nodes is high. -* {ccs-cap} is used. +The CSV export feature in Kibana makes queries to Elasticsearch and formats the results into CSV. This feature +offers a solution that attempts to provide the most benefit to the most use cases. However, things could go +wrong during export. Elasticsearch can stop responding, repeated querying can take so long that authentication +tokens can time out, and the format of exported data can be too complex for spreadsheet applications to handle. +Such situations are outside of the control of Kibana. If the use case becomes complex enough, it's recommended +that you create scripts that query Elasticsearch directly, using a scripting language like Python and the +public {es} APIs. -To export large amounts of data, use {es} APIs directly. -Check out the {ref}/point-in-time-api.html[Point in time API] or {ref}/sql-rest-format.html#_csv[SQL with CSV response data format]. -============ +For advice about common problems, refer to <<reporting-troubleshooting>>. [float] [[reporting-troubleshooting-csv-configure-scan-api]] diff --git a/docs/user/reporting/reporting-pdf-limitations.asciidoc b/docs/user/reporting/reporting-pdf-limitations.asciidoc new file mode 100644 index 0000000000000..3433b36eaf988 --- /dev/null +++ b/docs/user/reporting/reporting-pdf-limitations.asciidoc @@ -0,0 +1,7 @@ +We recommend using PNG/PDF reports to export moderate amounts of data only. The feature enables a high-level +export capability, but it's not intended for bulk export. If you need to export several pages of image data, +consider using multiple report jobs to export a small number of pages at a time. If the screenshot of exported +dashboard contains a large number of pixels, consider splitting the large dashboard into smaller artifacts to +use less memory and CPU resources. + +For the most reliable configuration of PDF/PNG {report-features}, consider installing {kib} using <<docker,Docker>> or using <<set-up-on-cloud,Elastic Cloud>>. diff --git a/docs/user/reporting/reporting-pdf-troubleshooting.asciidoc b/docs/user/reporting/reporting-pdf-troubleshooting.asciidoc index 9ea3ff6aa3721..771adf9f6c980 100644 --- a/docs/user/reporting/reporting-pdf-troubleshooting.asciidoc +++ b/docs/user/reporting/reporting-pdf-troubleshooting.asciidoc @@ -4,7 +4,10 @@ <titleabbrev>PDF/PNG</titleabbrev> ++++ -For the most reliable configuration of PDF/PNG {report-features}, consider installing {kib} using <<docker,Docker>> or using <<set-up-on-cloud,Elastic Cloud>>. +[NOTE] +============ +include::reporting-pdf-limitations.asciidoc[] +============ For more advice about common problems, refer to <<reporting-troubleshooting>>. @@ -99,10 +102,10 @@ Handle the generated output with care. [[reporting-troubleshooting-system-requirements]] === System requirements -In Elastic Cloud, the {kib} instances that most configurations provide by default is for 1GB of RAM for the instance. -That is enough for {kib} {report-features} when the visualization or dashboard is relatively simple, such as a single pie chart or a dashboard with a few visualizations. -However, certain visualization types incur more load than others. -For example, a TSVB panel has a lot of network requests to render. +In Elastic Cloud, the {kib} instances that most configurations provide by default is for 1GB of RAM for the +instance. That is enough for {kib} {report-features} when the visualization or dashboard is relatively simple, +such as a single pie chart or a dashboard with a few visualizations. However, certain visualization types +incur more load than others. For example, a TSVB panel has a lot of network requests to render. If the {kib} instance doesn't have enough memory to run the report, the report fails with an error such as `Error: Page crashed!`. In this case, try increasing the memory for the {kib} instance to 2GB. diff --git a/docs/user/troubleshooting/using-server-logs.asciidoc b/docs/user/troubleshooting/using-server-logs.asciidoc index 894b229d3f34d..b4dfe9e0bcd99 100644 --- a/docs/user/troubleshooting/using-server-logs.asciidoc +++ b/docs/user/troubleshooting/using-server-logs.asciidoc @@ -36,7 +36,7 @@ logging.loggers: ---- WARNING: Kibana's `file` appender is configured to produce logs in {ecs-ref}/ecs-reference.html[ECS JSON] format. It's the only format that includes the meta information necessary for {apm-node-ref}/log-correlation.html[log correlation] out-of-the-box. -The next step is to define what https://www.elastic.co/observability[observability tools] are available. +The next step is to define what https://www.elastic.co/observability[observability tools] are available. For a better experience, set up an https://www.elastic.co/guide/en/apm/get-started/current/observability-integrations.html[Observability integration] provided by Elastic to debug your application with the <<debugging-logs-apm-ui, APM UI.>> To debug something quickly without setting up additional tooling, you can work with <<plain-kibana-logs, the plain {kib} logs.>> @@ -47,7 +47,7 @@ To debug something quickly without setting up additional tooling, you can work w To debug {kib} with the APM UI, you must set up the APM infrastructure. You can find instructions for the setup process https://www.elastic.co/guide/en/apm/get-started/current/observability-integrations.html[on the Observability integrations page]. -Once you set up the APM infrastructure, you can enable the APM agent and put {kib} under load to collect APM events. To analyze the collected metrics and logs, use the APM UI as demonstrated {kibana-ref}/transactions.html#transaction-trace-sample[in the docs]. +Once you set up the APM infrastructure, you can enable the APM agent and put {kib} under load to collect APM events. To analyze the collected metrics and logs, use the APM UI as demonstrated {observability-guide}/apm-transactions.html#transaction-trace-sample[in the docs]. [[plain-kibana-logs]] ==== Plain {kib} logs diff --git a/examples/controls_example/public/app/react_control_example/react_control_example.tsx b/examples/controls_example/public/app/react_control_example/react_control_example.tsx index c3420cf22b609..76adcfb7e3c72 100644 --- a/examples/controls_example/public/app/react_control_example/react_control_example.tsx +++ b/examples/controls_example/public/app/react_control_example/react_control_example.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { BehaviorSubject, combineLatest, Subject } from 'rxjs'; - +import useMountedState from 'react-use/lib/useMountedState'; import { EuiBadge, EuiButton, @@ -76,6 +76,7 @@ export const ReactControlExample = ({ core: CoreStart; dataViews: DataViewsPublicPluginStart; }) => { + const isMounted = useMountedState(); const dataLoading$ = useMemo(() => { return new BehaviorSubject<boolean | undefined>(false); }, []); @@ -112,6 +113,7 @@ export const ReactControlExample = ({ const [controlGroupApi, setControlGroupApi] = useState<ControlGroupApi | undefined>(undefined); const [isControlGroupInitialized, setIsControlGroupInitialized] = useState(false); const [dataViewNotFound, setDataViewNotFound] = useState(false); + const [isResetting, setIsResetting] = useState(false); const dashboardApi = useMemo(() => { const query$ = new BehaviorSubject<Query | AggregateQuery | undefined>(undefined); @@ -361,9 +363,15 @@ export const ReactControlExample = ({ </EuiFlexItem> <EuiFlexItem grow={false}> <EuiButtonEmpty - isDisabled={!controlGroupApi} - onClick={() => { - controlGroupApi?.resetUnsavedChanges(); + isDisabled={!controlGroupApi || isResetting} + isLoading={isResetting} + onClick={async () => { + if (!controlGroupApi) { + return; + } + setIsResetting(true); + await controlGroupApi.asyncResetUnsavedChanges(); + if (isMounted()) setIsResetting(false); }} > Reset diff --git a/examples/controls_example/public/react_controls/control_group/control_group_unsaved_changes_api.ts b/examples/controls_example/public/react_controls/control_group/control_group_unsaved_changes_api.ts index 399fbf6c463cd..a0c5927872f10 100644 --- a/examples/controls_example/public/react_controls/control_group/control_group_unsaved_changes_api.ts +++ b/examples/controls_example/public/react_controls/control_group/control_group_unsaved_changes_api.ts @@ -20,6 +20,7 @@ import { import { combineLatest, map } from 'rxjs'; import { ControlsInOrder, getControlsInOrder } from './init_controls_manager'; import { ControlGroupRuntimeState, ControlPanelsState } from './types'; +import { apiPublishesAsyncFilters } from '../data_controls/publishes_async_filters'; export type ControlGroupComparatorState = Pick< ControlGroupRuntimeState, @@ -33,6 +34,7 @@ export type ControlGroupComparatorState = Pick< }; export function initializeControlGroupUnsavedChanges( + applySelections: () => void, children$: PresentationContainer['children$'], comparators: StateComparators<ControlGroupComparatorState>, snapshotControlsRuntimeState: () => ControlPanelsState, @@ -68,12 +70,25 @@ export function initializeControlGroupUnsavedChanges( return Object.keys(unsavedChanges).length ? unsavedChanges : undefined; }) ), - resetUnsavedChanges: () => { + asyncResetUnsavedChanges: async () => { controlGroupUnsavedChanges.api.resetUnsavedChanges(); + + const filtersReadyPromises: Array<Promise<void>> = []; Object.values(children$.value).forEach((controlApi) => { if (apiPublishesUnsavedChanges(controlApi)) controlApi.resetUnsavedChanges(); + if (apiPublishesAsyncFilters(controlApi)) { + filtersReadyPromises.push(controlApi.untilFiltersReady()); + } }); + + await Promise.all(filtersReadyPromises); + + if (!comparators.autoApplySelections[0].value) { + applySelections(); + } }, - } as PublishesUnsavedChanges<ControlGroupRuntimeState>, + } as Pick<PublishesUnsavedChanges, 'unsavedChanges'> & { + asyncResetUnsavedChanges: () => Promise<void>; + }, }; } diff --git a/examples/controls_example/public/react_controls/control_group/get_control_group_factory.tsx b/examples/controls_example/public/react_controls/control_group/get_control_group_factory.tsx index 13e6456071a47..896c9cebc641e 100644 --- a/examples/controls_example/public/react_controls/control_group/get_control_group_factory.tsx +++ b/examples/controls_example/public/react_controls/control_group/get_control_group_factory.tsx @@ -96,6 +96,7 @@ export const getControlGroupEmbeddableFactory = (services: { const dataLoading$ = new BehaviorSubject<boolean | undefined>(false); const unsavedChanges = initializeControlGroupUnsavedChanges( + selectionsManager.applySelections, controlsManager.api.children$, { ...controlsManager.comparators, diff --git a/examples/controls_example/public/react_controls/control_group/types.ts b/examples/controls_example/public/react_controls/control_group/types.ts index 65db5b8121b1b..17ce4f1890751 100644 --- a/examples/controls_example/public/react_controls/control_group/types.ts +++ b/examples/controls_example/public/react_controls/control_group/types.ts @@ -55,10 +55,11 @@ export type ControlGroupApi = PresentationContainer & HasSerializedChildState<ControlPanelState> & HasEditCapabilities & PublishesDataLoading & - PublishesUnsavedChanges & + Pick<PublishesUnsavedChanges, 'unsavedChanges'> & PublishesControlGroupDisplaySettings & PublishesTimeslice & Partial<HasParentApi<PublishesUnifiedSearch> & HasSaveNotification> & { + asyncResetUnsavedChanges: () => Promise<void>; autoApplySelections$: PublishingSubject<boolean>; controlFetch$: (controlUuid: string) => Observable<ControlFetchContext>; getLastSavedControlState: (controlUuid: string) => object; diff --git a/examples/controls_example/public/react_controls/data_controls/initialize_data_control.ts b/examples/controls_example/public/react_controls/data_controls/initialize_data_control.ts index 42119456bc0ef..c376890b232f5 100644 --- a/examples/controls_example/public/react_controls/data_controls/initialize_data_control.ts +++ b/examples/controls_example/public/react_controls/data_controls/initialize_data_control.ts @@ -7,7 +7,7 @@ */ import { isEqual } from 'lodash'; -import { BehaviorSubject, combineLatest, first, switchMap } from 'rxjs'; +import { BehaviorSubject, combineLatest, debounceTime, first, skip, switchMap, tap } from 'rxjs'; import { CoreStart } from '@kbn/core-lifecycle-browser'; import { @@ -45,9 +45,12 @@ export const initializeDataControl = <EditorState extends object = {}>( api: ControlApiInitialization<DataControlApi>; cleanup: () => void; comparators: StateComparators<DefaultDataControlState>; + setters: { + onSelectionChange: () => void; + setOutputFilter: (filter: Filter | undefined) => void; + }; stateManager: ControlStateManager<DefaultDataControlState>; serialize: () => SerializedPanelState<DefaultControlState>; - untilFiltersInitialized: () => Promise<void>; } => { const defaultControl = initializeDefaultControlApi(state); @@ -57,6 +60,7 @@ export const initializeDataControl = <EditorState extends object = {}>( const fieldName = new BehaviorSubject<string>(state.fieldName); const dataViews = new BehaviorSubject<DataView[] | undefined>(undefined); const filters$ = new BehaviorSubject<Filter[] | undefined>(undefined); + const filtersReady$ = new BehaviorSubject<boolean>(false); const field$ = new BehaviorSubject<DataViewField | undefined>(undefined); const fieldFormatter = new BehaviorSubject<DataControlFieldFormatter>((toFormat: any) => String(toFormat) @@ -69,14 +73,14 @@ export const initializeDataControl = <EditorState extends object = {}>( title: panelTitle, }; - function clearBlockingError() { - if (defaultControl.api.blockingError.value) { - defaultControl.api.setBlockingError(undefined); - } - } - const dataViewIdSubscription = dataViewId .pipe( + tap(() => { + filtersReady$.next(false); + if (defaultControl.api.blockingError.value) { + defaultControl.api.setBlockingError(undefined); + } + }), switchMap(async (currentDataViewId) => { let dataView: DataView | undefined; try { @@ -90,14 +94,17 @@ export const initializeDataControl = <EditorState extends object = {}>( .subscribe(({ dataView, error }) => { if (error) { defaultControl.api.setBlockingError(error); - } else { - clearBlockingError(); } dataViews.next(dataView ? [dataView] : undefined); }); - const fieldNameSubscription = combineLatest([dataViews, fieldName]).subscribe( - ([nextDataViews, nextFieldName]) => { + const fieldNameSubscription = combineLatest([dataViews, fieldName]) + .pipe( + tap(() => { + filtersReady$.next(false); + }) + ) + .subscribe(([nextDataViews, nextFieldName]) => { const dataView = nextDataViews ? nextDataViews.find(({ id }) => dataViewId.value === id) : undefined; @@ -115,8 +122,8 @@ export const initializeDataControl = <EditorState extends object = {}>( }) ) ); - } else { - clearBlockingError(); + } else if (defaultControl.api.blockingError.value) { + defaultControl.api.setBlockingError(undefined); } field$.next(field); @@ -125,8 +132,7 @@ export const initializeDataControl = <EditorState extends object = {}>( if (spec) { fieldFormatter.next(dataView.getFormatterForField(spec).getConverterFor('text')); } - } - ); + }); const onEdit = async () => { // get the initial state from the state manager @@ -172,6 +178,13 @@ export const initializeDataControl = <EditorState extends object = {}>( }); }; + const filtersReadySubscription = filters$.pipe(skip(1), debounceTime(0)).subscribe(() => { + // Set filtersReady$.next(true); in filters$ subscription instead of setOutputFilter + // to avoid signaling filters ready until after filters have been emitted + // to avoid timing issues + filtersReady$.next(true); + }); + const api: ControlApiInitialization<DataControlApi> = { ...defaultControl.api, panelTitle, @@ -181,10 +194,18 @@ export const initializeDataControl = <EditorState extends object = {}>( fieldFormatter, onEdit, filters$, - setOutputFilter: (newFilter: Filter | undefined) => { - filters$.next(newFilter ? [newFilter] : undefined); - }, isEditingEnabled: () => true, + untilFiltersReady: async () => { + return new Promise((resolve) => { + combineLatest([defaultControl.api.blockingError, filtersReady$]) + .pipe( + first(([blockingError, filtersReady]) => filtersReady || blockingError !== undefined) + ) + .subscribe(() => { + resolve(); + }); + }); + }, }; return { @@ -192,6 +213,7 @@ export const initializeDataControl = <EditorState extends object = {}>( cleanup: () => { dataViewIdSubscription.unsubscribe(); fieldNameSubscription.unsubscribe(); + filtersReadySubscription.unsubscribe(); }, comparators: { ...defaultControl.comparators, @@ -199,6 +221,14 @@ export const initializeDataControl = <EditorState extends object = {}>( dataViewId: [dataViewId, (value: string) => dataViewId.next(value)], fieldName: [fieldName, (value: string) => fieldName.next(value)], }, + setters: { + onSelectionChange: () => { + filtersReady$.next(false); + }, + setOutputFilter: (newFilter: Filter | undefined) => { + filters$.next(newFilter ? [newFilter] : undefined); + }, + }, stateManager, serialize: () => { return { @@ -217,19 +247,5 @@ export const initializeDataControl = <EditorState extends object = {}>( ], }; }, - untilFiltersInitialized: async () => { - return new Promise((resolve) => { - combineLatest([defaultControl.api.blockingError, filters$]) - .pipe( - first( - ([blockingError, filters]) => - blockingError !== undefined || (filters?.length ?? 0) > 0 - ) - ) - .subscribe(() => { - resolve(); - }); - }); - }, }; }; diff --git a/examples/controls_example/public/react_controls/data_controls/mocks/api_mocks.tsx b/examples/controls_example/public/react_controls/data_controls/mocks/api_mocks.tsx index 7f08a96e0ad71..a21e253f57628 100644 --- a/examples/controls_example/public/react_controls/data_controls/mocks/api_mocks.tsx +++ b/examples/controls_example/public/react_controls/data_controls/mocks/api_mocks.tsx @@ -11,12 +11,16 @@ import { BehaviorSubject } from 'rxjs'; import { OptionsListSuggestions } from '@kbn/controls-plugin/common/options_list/types'; import { DataViewField } from '@kbn/data-views-plugin/common'; +import { PublishingSubject } from '@kbn/presentation-publishing'; import { OptionsListSelection } from '../../../../common/options_list/options_list_selections'; import { OptionsListSearchTechnique } from '../../../../common/options_list/suggestions_searching'; import { OptionsListSortingType } from '../../../../common/options_list/suggestions_sorting'; import { OptionsListDisplaySettings } from '../options_list_control/types'; export const getOptionsListMocks = () => { + const selectedOptions$ = new BehaviorSubject<OptionsListSelection[] | undefined>(undefined); + const exclude$ = new BehaviorSubject<boolean | undefined>(undefined); + const existsSelected$ = new BehaviorSubject<boolean | undefined>(undefined); return { api: { uuid: 'testControl', @@ -30,17 +34,23 @@ export const getOptionsListMocks = () => { }, fieldFormatter: new BehaviorSubject((value: string | number) => String(value)), makeSelection: jest.fn(), + setExclude: (next: boolean | undefined) => exclude$.next(next), }, stateManager: { searchString: new BehaviorSubject<string>(''), searchStringValid: new BehaviorSubject<boolean>(true), fieldName: new BehaviorSubject<string>('field'), - exclude: new BehaviorSubject<boolean | undefined>(undefined), - existsSelected: new BehaviorSubject<boolean | undefined>(undefined), + exclude: exclude$ as PublishingSubject<boolean | undefined>, + existsSelected: existsSelected$ as PublishingSubject<boolean | undefined>, sort: new BehaviorSubject<OptionsListSortingType | undefined>(undefined), - selectedOptions: new BehaviorSubject<OptionsListSelection[] | undefined>(undefined), + selectedOptions: selectedOptions$ as PublishingSubject<OptionsListSelection[] | undefined>, searchTechnique: new BehaviorSubject<OptionsListSearchTechnique | undefined>(undefined), }, displaySettings: {} as OptionsListDisplaySettings, + // setSelectedOptions and setExistsSelected are not exposed via API because + // they are not used by components + // they are needed in tests however so expose them as top level keys + setSelectedOptions: (next: OptionsListSelection[] | undefined) => selectedOptions$.next(next), + setExistsSelected: (next: boolean | undefined) => existsSelected$.next(next), }; }; diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.test.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.test.tsx index c18233d85fc62..9cdf976150da7 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.test.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.test.tsx @@ -10,10 +10,9 @@ import React from 'react'; import { DataViewField } from '@kbn/data-views-plugin/common'; import { render } from '@testing-library/react'; -import { ControlStateManager } from '../../../types'; import { getOptionsListMocks } from '../../mocks/api_mocks'; -import { OptionsListControlContext } from '../options_list_context_provider'; -import { OptionsListComponentApi, OptionsListComponentState } from '../types'; +import { ContextStateManager, OptionsListControlContext } from '../options_list_context_provider'; +import { OptionsListComponentApi } from '../types'; import { OptionsListControl } from './options_list_control'; describe('Options list control', () => { @@ -31,7 +30,7 @@ describe('Options list control', () => { value={{ api: api as unknown as OptionsListComponentApi, displaySettings, - stateManager: stateManager as unknown as ControlStateManager<OptionsListComponentState>, + stateManager: stateManager as unknown as ContextStateManager, }} > <OptionsListControl controlPanelClassName="controlPanel" /> @@ -42,8 +41,8 @@ describe('Options list control', () => { test('if exclude = false and existsSelected = true, then the option should read "Exists"', async () => { const mocks = getOptionsListMocks(); mocks.api.uuid = 'testExists'; - mocks.stateManager.exclude.next(false); - mocks.stateManager.existsSelected.next(true); + mocks.api.setExclude(false); + mocks.setExistsSelected(true); const control = mountComponent(mocks); const existsOption = control.getByTestId('optionsList-control-testExists'); expect(existsOption).toHaveTextContent('Exists'); @@ -52,8 +51,8 @@ describe('Options list control', () => { test('if exclude = true and existsSelected = true, then the option should read "Does not exist"', async () => { const mocks = getOptionsListMocks(); mocks.api.uuid = 'testDoesNotExist'; - mocks.stateManager.exclude.next(true); - mocks.stateManager.existsSelected.next(true); + mocks.api.setExclude(true); + mocks.setExistsSelected(true); const control = mountComponent(mocks); const existsOption = control.getByTestId('optionsList-control-testDoesNotExist'); expect(existsOption).toHaveTextContent('DOES NOT Exist'); @@ -68,7 +67,7 @@ describe('Options list control', () => { { value: 'bark', docCount: 10 }, { value: 'meow', docCount: 12 }, ]); - mocks.stateManager.selectedOptions.next(['woof', 'bark']); + mocks.setSelectedOptions(['woof', 'bark']); mocks.api.field$.next({ name: 'Test keyword field', type: 'keyword', @@ -87,7 +86,7 @@ describe('Options list control', () => { { value: 2, docCount: 10 }, { value: 3, docCount: 12 }, ]); - mocks.stateManager.selectedOptions.next([1, 2]); + mocks.setSelectedOptions([1, 2]); mocks.api.field$.next({ name: 'Test keyword field', type: 'number', @@ -105,7 +104,7 @@ describe('Options list control', () => { { value: 'bark', docCount: 10 }, { value: 'meow', docCount: 12 }, ]); - mocks.stateManager.selectedOptions.next(['woof', 'bark']); + mocks.setSelectedOptions(['woof', 'bark']); mocks.api.invalidSelections$.next(new Set(['woof'])); mocks.api.field$.next({ name: 'Test keyword field', diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.tsx index 998ca612a34fb..c102f7822b804 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_control.tsx @@ -60,6 +60,7 @@ export const OptionsListControl = ({ api.panelTitle, api.fieldFormatter ); + const [defaultPanelTitle] = useBatchedOptionalPublishingSubjects(api.defaultPanelTitle); const delimiter = useMemo(() => OptionsListStrings.control.getSeparator(field?.type), [field]); diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover.test.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover.test.tsx index 05d601e093a4e..5e22b22077e52 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover.test.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover.test.tsx @@ -13,14 +13,9 @@ import { act, render, RenderResult, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { BehaviorSubject } from 'rxjs'; -import { ControlStateManager } from '../../../types'; import { getOptionsListMocks } from '../../mocks/api_mocks'; -import { OptionsListControlContext } from '../options_list_context_provider'; -import { - OptionsListComponentApi, - OptionsListComponentState, - OptionsListDisplaySettings, -} from '../types'; +import { ContextStateManager, OptionsListControlContext } from '../options_list_context_provider'; +import { OptionsListComponentApi, OptionsListDisplaySettings } from '../types'; import { OptionsListPopover } from './options_list_popover'; describe('Options list popover', () => { @@ -40,7 +35,7 @@ describe('Options list popover', () => { value={{ api: api as unknown as OptionsListComponentApi, displaySettings, - stateManager: stateManager as unknown as ControlStateManager<OptionsListComponentState>, + stateManager: stateManager as unknown as ContextStateManager, }} > <OptionsListPopover /> @@ -83,7 +78,7 @@ describe('Options list popover', () => { expect(mocks.api.makeSelection).toBeCalledWith('woof', false); // simulate `makeSelection` - mocks.stateManager.selectedOptions.next(['woof']); + mocks.setSelectedOptions(['woof']); await waitOneTick(); clickShowOnlySelections(popover); @@ -102,7 +97,7 @@ describe('Options list popover', () => { { value: 'meow', docCount: 12 }, ]); const popover = mountComponent(mocks); - mocks.stateManager.selectedOptions.next(selections); + mocks.setSelectedOptions(selections); await waitOneTick(); clickShowOnlySelections(popover); @@ -121,7 +116,7 @@ describe('Options list popover', () => { { value: 'bark', docCount: 10 }, { value: 'meow', docCount: 12 }, ]); - mocks.stateManager.selectedOptions.next([]); + mocks.setSelectedOptions([]); const popover = mountComponent(mocks); clickShowOnlySelections(popover); @@ -139,7 +134,7 @@ describe('Options list popover', () => { { value: 'bark', docCount: 10 }, { value: 'meow', docCount: 12 }, ]); - mocks.stateManager.selectedOptions.next(['woof', 'bark']); + mocks.setSelectedOptions(['woof', 'bark']); const popover = mountComponent(mocks); let searchBox = popover.getByTestId('optionsList-control-search-input'); @@ -163,7 +158,7 @@ describe('Options list popover', () => { { value: 'bark', docCount: 75 }, ]); const popover = mountComponent(mocks); - mocks.stateManager.selectedOptions.next(['woof', 'bark']); + mocks.setSelectedOptions(['woof', 'bark']); mocks.api.invalidSelections$.next(new Set(['woof'])); await waitOneTick(); @@ -185,7 +180,7 @@ describe('Options list popover', () => { { value: 'woof', docCount: 5 }, { value: 'bark', docCount: 75 }, ]); - mocks.stateManager.selectedOptions.next(['bark', 'woof', 'meow']); + mocks.setSelectedOptions(['bark', 'woof', 'meow']); mocks.api.invalidSelections$.next(new Set(['woof', 'meow'])); const popover = mountComponent(mocks); @@ -207,7 +202,7 @@ describe('Options list popover', () => { test('if exclude = true, select appropriate button in button group', async () => { const mocks = getOptionsListMocks(); const popover = mountComponent(mocks); - mocks.stateManager.exclude.next(true); + mocks.api.setExclude(true); await waitOneTick(); const includeButton = popover.getByTestId('optionsList__includeResults'); @@ -223,7 +218,7 @@ describe('Options list popover', () => { mocks.api.availableOptions$.next([]); const popover = mountComponent(mocks); - mocks.stateManager.existsSelected.next(false); + mocks.setExistsSelected(false); await waitOneTick(); const existsOption = popover.queryByTestId('optionsList-control-selection-exists'); @@ -238,7 +233,7 @@ describe('Options list popover', () => { ]); const popover = mountComponent(mocks); - mocks.stateManager.existsSelected.next(true); + mocks.setExistsSelected(true); await waitOneTick(); clickShowOnlySelections(popover); diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_footer.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_footer.tsx index aa38330908762..67f984cd69905 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_footer.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_footer.tsx @@ -78,9 +78,7 @@ export const OptionsListPopoverFooter = () => { legend={OptionsListStrings.popover.getIncludeExcludeLegend()} options={aggregationToggleButtons} idSelected={exclude ? 'optionsList__excludeResults' : 'optionsList__includeResults'} - onChange={(optionId) => - stateManager.exclude.next(optionId === 'optionsList__excludeResults') - } + onChange={(optionId) => api.setExclude(optionId === 'optionsList__excludeResults')} buttonSize="compressed" data-test-subj="optionsList__includeExcludeButtonGroup" /> diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_sorting_button.test.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_sorting_button.test.tsx index c86aa85b9116e..b7d59a1e91923 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_sorting_button.test.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_sorting_button.test.tsx @@ -12,10 +12,9 @@ import { DataViewField } from '@kbn/data-views-plugin/common'; import { render, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { ControlStateManager } from '../../../types'; import { getOptionsListMocks } from '../../mocks/api_mocks'; -import { OptionsListControlContext } from '../options_list_context_provider'; -import { OptionsListComponentApi, OptionsListComponentState } from '../types'; +import { ContextStateManager, OptionsListControlContext } from '../options_list_context_provider'; +import { OptionsListComponentApi } from '../types'; import { OptionsListPopoverSortingButton } from './options_list_popover_sorting_button'; describe('Options list sorting button', () => { @@ -33,7 +32,7 @@ describe('Options list sorting button', () => { value={{ api: api as unknown as OptionsListComponentApi, displaySettings, - stateManager: stateManager as unknown as ControlStateManager<OptionsListComponentState>, + stateManager: stateManager as unknown as ContextStateManager, }} > <OptionsListPopoverSortingButton showOnlySelected={false} /> diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx index 3fdce47271873..a8cd84252e0d9 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/components/options_list_popover_suggestions.tsx @@ -196,7 +196,6 @@ export const OptionsListPopoverSuggestions = ({ )} emptyMessage={<OptionsListPopoverEmptyMessage showOnlySelected={showOnlySelected} />} onChange={(newSuggestions, _, changedOption) => { - setSelectableOptions(newSuggestions); api.makeSelection(changedOption.key, showOnlySelected); }} > diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/fetch_and_validate.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/fetch_and_validate.tsx index 5ed0d00623706..6f02e26864184 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/fetch_and_validate.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/fetch_and_validate.tsx @@ -18,7 +18,9 @@ import { import { OptionsListSuccessResponse } from '@kbn/controls-plugin/common/options_list/types'; +import { PublishingSubject } from '@kbn/presentation-publishing'; import { isValidSearch } from '../../../../common/options_list/suggestions_searching'; +import { OptionsListSelection } from '../../../../common/options_list/options_list_selections'; import { ControlFetchContext } from '../../control_group/control_fetch'; import { ControlStateManager } from '../../types'; import { DataControlServices } from '../types'; @@ -37,7 +39,11 @@ export function fetchAndValidate$({ debouncedSearchString: Observable<string>; }; services: DataControlServices; - stateManager: ControlStateManager<OptionsListComponentState>; + stateManager: ControlStateManager< + Pick<OptionsListComponentState, 'requestSize' | 'runPastTimeout' | 'searchTechnique' | 'sort'> + > & { + selectedOptions: PublishingSubject<OptionsListSelection[] | undefined>; + }; }): Observable<OptionsListSuccessResponse | { error: Error }> { const requestCache = new OptionsListFetchCache(); let abortController: AbortController | undefined; diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/get_options_list_control_factory.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/get_options_list_control_factory.tsx index 0e0d28cd96a33..e71bcd9002c36 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/get_options_list_control_factory.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/get_options_list_control_factory.tsx @@ -7,7 +7,6 @@ */ import React, { useEffect } from 'react'; -import deepEqual from 'react-fast-compare'; import { BehaviorSubject, combineLatest, debounceTime, filter, skip } from 'rxjs'; import { OptionsListSearchTechnique } from '@kbn/controls-plugin/common/options_list/suggestions_searching'; @@ -38,6 +37,7 @@ import { fetchAndValidate$ } from './fetch_and_validate'; import { OptionsListControlContext } from './options_list_context_provider'; import { OptionsListStrings } from './options_list_strings'; import { OptionsListControlApi, OptionsListControlState } from './types'; +import { initializeOptionsListSelections } from './options_list_control_selections'; export const getOptionsListControlFactory = ( services: DataControlServices @@ -62,14 +62,9 @@ export const getOptionsListControlFactory = ( ); const runPastTimeout$ = new BehaviorSubject<boolean | undefined>(initialState.runPastTimeout); const singleSelect$ = new BehaviorSubject<boolean | undefined>(initialState.singleSelect); - const selections$ = new BehaviorSubject<OptionsListSelection[] | undefined>( - initialState.selectedOptions ?? [] - ); const sort$ = new BehaviorSubject<OptionsListSortingType | undefined>( initialState.sort ?? OPTIONS_LIST_DEFAULT_SORT ); - const existsSelected$ = new BehaviorSubject<boolean | undefined>(initialState.existsSelected); - const excludeSelected$ = new BehaviorSubject<boolean | undefined>(initialState.exclude); /** Creation options state - cannot currently be changed after creation, but need subjects for comparators */ const placeholder$ = new BehaviorSubject<string | undefined>(initialState.placeholder); @@ -98,12 +93,17 @@ export const getOptionsListControlFactory = ( services ); + const selections = initializeOptionsListSelections( + initialState, + dataControl.setters.onSelectionChange + ); + const stateManager = { ...dataControl.stateManager, - exclude: excludeSelected$, - existsSelected: existsSelected$, + exclude: selections.exclude$, + existsSelected: selections.existsSelected$, searchTechnique: searchTechnique$, - selectedOptions: selections$, + selectedOptions: selections.selectedOptions$, singleSelect: singleSelect$, sort: sort$, searchString: searchString$, @@ -150,9 +150,9 @@ export const getOptionsListControlFactory = ( ) .subscribe(() => { searchString$.next(''); - selections$.next(undefined); - existsSelected$.next(false); - excludeSelected$.next(false); + selections.setSelectedOptions(undefined); + selections.setExistsSelected(false); + selections.setExclude(false); requestSize$.next(MIN_OPTIONS_LIST_REQUEST_SIZE); sort$.next(OPTIONS_LIST_DEFAULT_SORT); }); @@ -196,38 +196,40 @@ export const getOptionsListControlFactory = ( const singleSelectSubscription = singleSelect$ .pipe(filter((singleSelect) => Boolean(singleSelect))) .subscribe(() => { - const currentSelections = selections$.getValue() ?? []; - if (currentSelections.length > 1) selections$.next([currentSelections[0]]); + const currentSelections = selections.selectedOptions$.getValue() ?? []; + if (currentSelections.length > 1) selections.setSelectedOptions([currentSelections[0]]); }); /** Output filters when selections change */ const outputFilterSubscription = combineLatest([ dataControl.api.dataViews, dataControl.stateManager.fieldName, - selections$, - existsSelected$, - excludeSelected$, - ]).subscribe(([dataViews, fieldName, selections, existsSelected, exclude]) => { - const dataView = dataViews?.[0]; - const field = dataView && fieldName ? dataView.getFieldByName(fieldName) : undefined; - - if (!dataView || !field) return; - - let newFilter: Filter | undefined; - if (existsSelected) { - newFilter = buildExistsFilter(field, dataView); - } else if (selections && selections.length > 0) { - newFilter = - selections.length === 1 - ? buildPhraseFilter(field, selections[0], dataView) - : buildPhrasesFilter(field, selections, dataView); - } - if (newFilter) { - newFilter.meta.key = field?.name; - if (exclude) newFilter.meta.negate = true; - } - api.setOutputFilter(newFilter); - }); + selections.selectedOptions$, + selections.existsSelected$, + selections.exclude$, + ]) + .pipe(debounceTime(0)) + .subscribe(([dataViews, fieldName, selectedOptions, existsSelected, exclude]) => { + const dataView = dataViews?.[0]; + const field = dataView && fieldName ? dataView.getFieldByName(fieldName) : undefined; + + let newFilter: Filter | undefined; + if (dataView && field) { + if (existsSelected) { + newFilter = buildExistsFilter(field, dataView); + } else if (selectedOptions && selectedOptions.length > 0) { + newFilter = + selectedOptions.length === 1 + ? buildPhraseFilter(field, selectedOptions[0], dataView) + : buildPhrasesFilter(field, selectedOptions, dataView); + } + } + if (newFilter) { + newFilter.meta.key = field?.name; + if (exclude) newFilter.meta.negate = true; + } + dataControl.setters.setOutputFilter(newFilter); + }); const api = buildApi( { @@ -241,10 +243,10 @@ export const getOptionsListControlFactory = ( searchTechnique: searchTechnique$.getValue(), runPastTimeout: runPastTimeout$.getValue(), singleSelect: singleSelect$.getValue(), - selections: selections$.getValue(), + selections: selections.selectedOptions$.getValue(), sort: sort$.getValue(), - existsSelected: existsSelected$.getValue(), - exclude: excludeSelected$.getValue(), + existsSelected: selections.existsSelected$.getValue(), + exclude: selections.exclude$.getValue(), // serialize state that cannot be changed to keep it consistent placeholder: placeholder$.getValue(), @@ -257,26 +259,20 @@ export const getOptionsListControlFactory = ( }; }, clearSelections: () => { - if (selections$.getValue()?.length) selections$.next([]); - if (existsSelected$.getValue()) existsSelected$.next(false); + if (selections.selectedOptions$.getValue()?.length) selections.setSelectedOptions([]); + if (selections.existsSelected$.getValue()) selections.setExistsSelected(false); if (invalidSelections$.getValue().size) invalidSelections$.next(new Set([])); }, }, { ...dataControl.comparators, - exclude: [excludeSelected$, (selected) => excludeSelected$.next(selected)], - existsSelected: [existsSelected$, (selected) => existsSelected$.next(selected)], + ...selections.comparators, runPastTimeout: [runPastTimeout$, (runPast) => runPastTimeout$.next(runPast)], searchTechnique: [ searchTechnique$, (technique) => searchTechnique$.next(technique), (a, b) => (a ?? DEFAULT_SEARCH_TECHNIQUE) === (b ?? DEFAULT_SEARCH_TECHNIQUE), ], - selectedOptions: [ - selections$, - (selections) => selections$.next(selections), - (a, b) => deepEqual(a ?? [], b ?? []), - ], singleSelect: [singleSelect$, (selected) => singleSelect$.next(selected)], sort: [ sort$, @@ -295,11 +291,11 @@ export const getOptionsListControlFactory = ( const componentApi = { ...api, - selections$, loadMoreSubject, totalCardinality$, availableOptions$, invalidSelections$, + setExclude: selections.setExclude, deselectOption: (key: string | undefined) => { const field = api.field$.getValue(); if (!key || !field) { @@ -312,12 +308,12 @@ export const getOptionsListControlFactory = ( const keyAsType = getSelectionAsFieldType(field, key); // delete from selections - const selectedOptions = selections$.getValue() ?? []; - const itemIndex = (selections$.getValue() ?? []).indexOf(keyAsType); + const selectedOptions = selections.selectedOptions$.getValue() ?? []; + const itemIndex = (selections.selectedOptions$.getValue() ?? []).indexOf(keyAsType); if (itemIndex !== -1) { const newSelections = [...selectedOptions]; newSelections.splice(itemIndex, 1); - selections$.next(newSelections); + selections.setSelectedOptions(newSelections); } // delete from invalid selections const currentInvalid = invalidSelections$.getValue(); @@ -335,37 +331,37 @@ export const getOptionsListControlFactory = ( return; } - const existsSelected = Boolean(existsSelected$.getValue()); - const selectedOptions = selections$.getValue() ?? []; + const existsSelected = Boolean(selections.existsSelected$.getValue()); + const selectedOptions = selections.selectedOptions$.getValue() ?? []; const singleSelect = singleSelect$.getValue(); // the order of these checks matters, so be careful if rearranging them const keyAsType = getSelectionAsFieldType(field, key); if (key === 'exists-option') { // if selecting exists, then deselect everything else - existsSelected$.next(!existsSelected); + selections.setExistsSelected(!existsSelected); if (!existsSelected) { - selections$.next([]); + selections.setSelectedOptions([]); invalidSelections$.next(new Set([])); } } else if (showOnlySelected || selectedOptions.includes(keyAsType)) { componentApi.deselectOption(key); } else if (singleSelect) { // replace selection - selections$.next([keyAsType]); - if (existsSelected) existsSelected$.next(false); + selections.setSelectedOptions([keyAsType]); + if (existsSelected) selections.setExistsSelected(false); } else { // select option - if (!selectedOptions) selections$.next([]); - if (existsSelected) existsSelected$.next(false); - selections$.next([...selectedOptions, keyAsType]); + if (existsSelected) selections.setExistsSelected(false); + selections.setSelectedOptions( + selectedOptions ? [...selectedOptions, keyAsType] : [keyAsType] + ); } }, }; - if (initialState.selectedOptions?.length || initialState.existsSelected) { - // has selections, so wait for initialization of filters - await dataControl.untilFiltersInitialized(); + if (selections.hasInitialSelections) { + await dataControl.api.untilFiltersReady(); } return { diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/options_list_context_provider.tsx b/examples/controls_example/public/react_controls/data_controls/options_list_control/options_list_context_provider.tsx index 71783210bddfb..4c992331e6a5a 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/options_list_context_provider.tsx +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/options_list_context_provider.tsx @@ -8,17 +8,27 @@ import React, { useContext } from 'react'; +import { PublishingSubject } from '@kbn/presentation-publishing'; import { ControlStateManager } from '../../types'; import { OptionsListComponentApi, OptionsListComponentState, OptionsListDisplaySettings, } from './types'; +import { OptionsListSelection } from '../../../../common/options_list/options_list_selections'; + +export type ContextStateManager = ControlStateManager< + Omit<OptionsListComponentState, 'exclude' | 'existsSelected' | 'selectedOptions'> +> & { + selectedOptions: PublishingSubject<OptionsListSelection[] | undefined>; + existsSelected: PublishingSubject<boolean | undefined>; + exclude: PublishingSubject<boolean | undefined>; +}; export const OptionsListControlContext = React.createContext< | { api: OptionsListComponentApi; - stateManager: ControlStateManager<OptionsListComponentState>; + stateManager: ContextStateManager; displaySettings: OptionsListDisplaySettings; } | undefined diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/options_list_control_selections.ts b/examples/controls_example/public/react_controls/data_controls/options_list_control/options_list_control_selections.ts new file mode 100644 index 0000000000000..58efa05110844 --- /dev/null +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/options_list_control_selections.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { BehaviorSubject } from 'rxjs'; +import deepEqual from 'react-fast-compare'; +import { PublishingSubject, StateComparators } from '@kbn/presentation-publishing'; +import { OptionsListControlState } from './types'; +import { OptionsListSelection } from '../../../../common/options_list/options_list_selections'; + +export function initializeOptionsListSelections( + initialState: OptionsListControlState, + onSelectionChange: () => void +) { + const selectedOptions$ = new BehaviorSubject<OptionsListSelection[] | undefined>( + initialState.selectedOptions ?? [] + ); + const selectedOptionsComparatorFunction = ( + a: OptionsListSelection[] | undefined, + b: OptionsListSelection[] | undefined + ) => deepEqual(a ?? [], b ?? []); + function setSelectedOptions(next: OptionsListSelection[] | undefined) { + if (!selectedOptionsComparatorFunction(selectedOptions$.value, next)) { + selectedOptions$.next(next); + onSelectionChange(); + } + } + + const existsSelected$ = new BehaviorSubject<boolean | undefined>(initialState.existsSelected); + function setExistsSelected(next: boolean | undefined) { + if (existsSelected$.value !== next) { + existsSelected$.next(next); + onSelectionChange(); + } + } + + const exclude$ = new BehaviorSubject<boolean | undefined>(initialState.exclude); + function setExclude(next: boolean | undefined) { + if (exclude$.value !== next) { + exclude$.next(next); + onSelectionChange(); + } + } + + return { + comparators: { + exclude: [exclude$, setExclude], + existsSelected: [existsSelected$, setExistsSelected], + selectedOptions: [selectedOptions$, setSelectedOptions, selectedOptionsComparatorFunction], + } as StateComparators< + Pick<OptionsListControlState, 'exclude' | 'existsSelected' | 'selectedOptions'> + >, + hasInitialSelections: initialState.selectedOptions?.length || initialState.existsSelected, + selectedOptions$: selectedOptions$ as PublishingSubject<OptionsListSelection[] | undefined>, + setSelectedOptions, + existsSelected$: existsSelected$ as PublishingSubject<boolean | undefined>, + setExistsSelected, + exclude$: exclude$ as PublishingSubject<boolean | undefined>, + setExclude, + }; +} diff --git a/examples/controls_example/public/react_controls/data_controls/options_list_control/types.ts b/examples/controls_example/public/react_controls/data_controls/options_list_control/types.ts index 3fba2f6908d0c..b2e2f9c4c75b1 100644 --- a/examples/controls_example/public/react_controls/data_controls/options_list_control/types.ts +++ b/examples/controls_example/public/react_controls/data_controls/options_list_control/types.ts @@ -56,4 +56,5 @@ export type OptionsListComponentApi = OptionsListControlApi & deselectOption: (key: string | undefined) => void; makeSelection: (key: string | undefined, showOnlySelected: boolean) => void; loadMoreSubject: BehaviorSubject<null>; + setExclude: (next: boolean | undefined) => void; }; diff --git a/examples/controls_example/public/react_controls/data_controls/publishes_async_filters.ts b/examples/controls_example/public/react_controls/data_controls/publishes_async_filters.ts new file mode 100644 index 0000000000000..18e94d048f98f --- /dev/null +++ b/examples/controls_example/public/react_controls/data_controls/publishes_async_filters.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 { PublishesFilters, apiPublishesFilters } from '@kbn/presentation-publishing'; + +/** + * Data control filter generation is async because + * 1) filter generation requires a DataView + * 2) filter generation is a subscription + */ +export type PublishesAsyncFilters = PublishesFilters & { + untilFiltersReady: () => Promise<void>; +}; + +export const apiPublishesAsyncFilters = ( + unknownApi: unknown +): unknownApi is PublishesAsyncFilters => { + return Boolean( + unknownApi && + apiPublishesFilters(unknownApi) && + (unknownApi as PublishesAsyncFilters)?.untilFiltersReady !== undefined + ); +}; diff --git a/examples/controls_example/public/react_controls/data_controls/range_slider/get_range_slider_control_factory.tsx b/examples/controls_example/public/react_controls/data_controls/range_slider/get_range_slider_control_factory.tsx index 385a93cf7e1d5..a9b3e31a2c706 100644 --- a/examples/controls_example/public/react_controls/data_controls/range_slider/get_range_slider_control_factory.tsx +++ b/examples/controls_example/public/react_controls/data_controls/range_slider/get_range_slider_control_factory.tsx @@ -10,19 +10,15 @@ import React, { useEffect, useState } from 'react'; import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; import { buildRangeFilter, Filter, RangeFilterParams } from '@kbn/es-query'; import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; -import { BehaviorSubject, combineLatest, map, skip } from 'rxjs'; +import { BehaviorSubject, combineLatest, debounceTime, map, skip } from 'rxjs'; import { initializeDataControl } from '../initialize_data_control'; import { DataControlFactory, DataControlServices } from '../types'; import { RangeSliderControl } from './components/range_slider_control'; import { hasNoResults$ } from './has_no_results'; import { minMax$ } from './min_max'; import { RangeSliderStrings } from './range_slider_strings'; -import { - RangesliderControlApi, - RangesliderControlState, - RangeValue, - RANGE_SLIDER_CONTROL_TYPE, -} from './types'; +import { RangesliderControlApi, RangesliderControlState, RANGE_SLIDER_CONTROL_TYPE } from './types'; +import { initializeRangeControlSelections } from './range_control_selections'; export const getRangesliderControlFactory = ( services: DataControlServices @@ -62,10 +58,6 @@ export const getRangesliderControlFactory = ( const loadingHasNoResults$ = new BehaviorSubject<boolean>(false); const dataLoading$ = new BehaviorSubject<boolean | undefined>(undefined); const step$ = new BehaviorSubject<number | undefined>(initialState.step ?? 1); - const value$ = new BehaviorSubject<RangeValue | undefined>(initialState.value); - function setValue(nextValue: RangeValue | undefined) { - value$.next(nextValue); - } const dataControl = initializeDataControl<Pick<RangesliderControlState, 'step'>>( uuid, @@ -78,6 +70,11 @@ export const getRangesliderControlFactory = ( services ); + const selections = initializeRangeControlSelections( + initialState, + dataControl.setters.onSelectionChange + ); + const api = buildApi( { ...dataControl.api, @@ -89,23 +86,23 @@ export const getRangesliderControlFactory = ( rawState: { ...dataControlState, step: step$.getValue(), - value: value$.getValue(), + value: selections.value$.getValue(), }, references, // does not have any references other than those provided by the data control serializer }; }, clearSelections: () => { - value$.next(undefined); + selections.setValue(undefined); }, }, { ...dataControl.comparators, + ...selections.comparators, step: [ step$, (nextStep: number | undefined) => step$.next(nextStep), (a, b) => (a ?? 1) === (b ?? 1), ], - value: [value$, setValue], } ); @@ -129,7 +126,7 @@ export const getRangesliderControlFactory = ( .pipe(skip(1)) .subscribe(() => { step$.next(1); - value$.next(undefined); + selections.setValue(undefined); }); const max$ = new BehaviorSubject<number | undefined>(undefined); @@ -167,28 +164,30 @@ export const getRangesliderControlFactory = ( const outputFilterSubscription = combineLatest([ dataControl.api.dataViews, dataControl.stateManager.fieldName, - value$, - ]).subscribe(([dataViews, fieldName, value]) => { - const dataView = dataViews?.[0]; - const dataViewField = - dataView && fieldName ? dataView.getFieldByName(fieldName) : undefined; - const gte = parseFloat(value?.[0] ?? ''); - const lte = parseFloat(value?.[1] ?? ''); - - let rangeFilter: Filter | undefined; - if (value && dataView && dataViewField && !isNaN(gte) && !isNaN(lte)) { - const params = { - gte, - lte, - } as RangeFilterParams; - - rangeFilter = buildRangeFilter(dataViewField, params, dataView); - rangeFilter.meta.key = fieldName; - rangeFilter.meta.type = 'range'; - rangeFilter.meta.params = params; - } - api.setOutputFilter(rangeFilter); - }); + selections.value$, + ]) + .pipe(debounceTime(0)) + .subscribe(([dataViews, fieldName, value]) => { + const dataView = dataViews?.[0]; + const dataViewField = + dataView && fieldName ? dataView.getFieldByName(fieldName) : undefined; + const gte = parseFloat(value?.[0] ?? ''); + const lte = parseFloat(value?.[1] ?? ''); + + let rangeFilter: Filter | undefined; + if (value && dataView && dataViewField && !isNaN(gte) && !isNaN(lte)) { + const params = { + gte, + lte, + } as RangeFilterParams; + + rangeFilter = buildRangeFilter(dataViewField, params, dataView); + rangeFilter.meta.key = fieldName; + rangeFilter.meta.type = 'range'; + rangeFilter.meta.params = params; + } + dataControl.setters.setOutputFilter(rangeFilter); + }); const selectionHasNoResults$ = new BehaviorSubject(false); const hasNotResultsSubscription = hasNoResults$({ @@ -204,8 +203,8 @@ export const getRangesliderControlFactory = ( selectionHasNoResults$.next(hasNoResults); }); - if (initialState.value !== undefined) { - await dataControl.untilFiltersInitialized(); + if (selections.hasInitialSelections) { + await dataControl.api.untilFiltersReady(); } return { @@ -219,7 +218,7 @@ export const getRangesliderControlFactory = ( min$, selectionHasNoResults$, step$, - value$ + selections.value$ ); useEffect(() => { @@ -240,7 +239,7 @@ export const getRangesliderControlFactory = ( isLoading={typeof dataLoading === 'boolean' ? dataLoading : false} max={max} min={min} - onChange={setValue} + onChange={selections.setValue} step={step ?? 1} value={value} uuid={uuid} diff --git a/examples/controls_example/public/react_controls/data_controls/range_slider/range_control_selections.ts b/examples/controls_example/public/react_controls/data_controls/range_slider/range_control_selections.ts new file mode 100644 index 0000000000000..b8c88b249c799 --- /dev/null +++ b/examples/controls_example/public/react_controls/data_controls/range_slider/range_control_selections.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 { BehaviorSubject } from 'rxjs'; +import { PublishingSubject, StateComparators } from '@kbn/presentation-publishing'; +import { RangeValue, RangesliderControlState } from './types'; + +export function initializeRangeControlSelections( + initialState: RangesliderControlState, + onSelectionChange: () => void +) { + const value$ = new BehaviorSubject<RangeValue | undefined>(initialState.value); + function setValue(next: RangeValue | undefined) { + if (value$.value !== next) { + value$.next(next); + onSelectionChange(); + } + } + + return { + comparators: { + value: [value$, setValue], + } as StateComparators<Pick<RangesliderControlState, 'value'>>, + hasInitialSelections: initialState.value !== undefined, + value$: value$ as PublishingSubject<RangeValue | undefined>, + setValue, + }; +} diff --git a/examples/controls_example/public/react_controls/data_controls/search_control/get_search_control_factory.tsx b/examples/controls_example/public/react_controls/data_controls/search_control/get_search_control_factory.tsx index 6eed314ccdaa2..4d00e9834c349 100644 --- a/examples/controls_example/public/react_controls/data_controls/search_control/get_search_control_factory.tsx +++ b/examples/controls_example/public/react_controls/data_controls/search_control/get_search_control_factory.tsx @@ -7,8 +7,7 @@ */ import React, { useEffect, useState } from 'react'; -import deepEqual from 'react-fast-compare'; -import { BehaviorSubject, combineLatest, debounceTime, distinctUntilChanged, skip } from 'rxjs'; +import { BehaviorSubject, combineLatest, debounceTime, skip } from 'rxjs'; import { EuiFieldSearch, EuiFormRow, EuiRadioGroup } from '@elastic/eui'; import { css } from '@emotion/react'; @@ -16,6 +15,7 @@ import { i18n } from '@kbn/i18n'; import { useStateFromPublishingSubject } from '@kbn/presentation-publishing'; import { euiThemeVars } from '@kbn/ui-theme'; +import { Filter } from '@kbn/es-query'; import { initializeDataControl } from '../initialize_data_control'; import { DataControlFactory, DataControlServices } from '../types'; import { @@ -24,6 +24,7 @@ import { SearchControlTechniques, SEARCH_CONTROL_TYPE, } from './types'; +import { initializeSearchControlSelections } from './search_control_selections'; const allSearchOptions = [ { @@ -79,7 +80,6 @@ export const getSearchControlFactory = ( ); }, buildControl: async (initialState, buildApi, uuid, parentApi) => { - const searchString = new BehaviorSubject<string | undefined>(initialState.searchString); const searchTechnique = new BehaviorSubject<SearchControlTechniques | undefined>( initialState.searchTechnique ?? DEFAULT_SEARCH_TECHNIQUE ); @@ -94,6 +94,11 @@ export const getSearchControlFactory = ( services ); + const selections = initializeSearchControlSelections( + initialState, + dataControl.setters.onSelectionChange + ); + const api = buildApi( { ...dataControl.api, @@ -106,64 +111,58 @@ export const getSearchControlFactory = ( return { rawState: { ...dataControlState, - searchString: searchString.getValue(), + searchString: selections.searchString$.getValue(), searchTechnique: searchTechnique.getValue(), }, references, // does not have any references other than those provided by the data control serializer }; }, clearSelections: () => { - searchString.next(undefined); + selections.setSearchString(undefined); }, }, { ...dataControl.comparators, + ...selections.comparators, searchTechnique: [ searchTechnique, (newTechnique: SearchControlTechniques | undefined) => searchTechnique.next(newTechnique), (a, b) => (a ?? DEFAULT_SEARCH_TECHNIQUE) === (b ?? DEFAULT_SEARCH_TECHNIQUE), ], - searchString: [ - searchString, - (newString: string | undefined) => - searchString.next(newString?.length === 0 ? undefined : newString), - ], } ); /** * If either the search string or the search technique changes, recalulate the output filter */ - const onSearchStringChanged = combineLatest([searchString, searchTechnique]) - .pipe(debounceTime(200), distinctUntilChanged(deepEqual)) + const onSearchStringChanged = combineLatest([selections.searchString$, searchTechnique]) + .pipe(debounceTime(200)) .subscribe(([newSearchString, currentSearchTechnnique]) => { const currentDataView = dataControl.api.dataViews.getValue()?.[0]; const currentField = dataControl.stateManager.fieldName.getValue(); - if (currentDataView && currentField) { - if (newSearchString) { - api.setOutputFilter( - currentSearchTechnnique === 'match' - ? { - query: { match: { [currentField]: { query: newSearchString } } }, - meta: { index: currentDataView.id }, - } - : { - query: { - simple_query_string: { - query: newSearchString, - fields: [currentField], - default_operator: 'and', - }, + let filter: Filter | undefined; + if (currentDataView && currentField && newSearchString) { + filter = + currentSearchTechnnique === 'match' + ? { + query: { match: { [currentField]: { query: newSearchString } } }, + meta: { index: currentDataView.id }, + } + : { + query: { + simple_query_string: { + query: newSearchString, + fields: [currentField], + default_operator: 'and', }, - meta: { index: currentDataView.id }, - } - ); - } else { - api.setOutputFilter(undefined); - } + }, + meta: { index: currentDataView.id }, + }; } + + dataControl.setters.setOutputFilter(filter); }); /** @@ -176,11 +175,11 @@ export const getSearchControlFactory = ( ]) .pipe(skip(1)) .subscribe(() => { - searchString.next(undefined); + selections.setSearchString(undefined); }); if (initialState.searchString?.length) { - await dataControl.untilFiltersInitialized(); + await dataControl.api.untilFiltersReady(); } return { @@ -190,7 +189,7 @@ export const getSearchControlFactory = ( * ControlPanel that are necessary for styling */ Component: ({ className: controlPanelClassName }) => { - const currentSearch = useStateFromPublishingSubject(searchString); + const currentSearch = useStateFromPublishingSubject(selections.searchString$); useEffect(() => { return () => { @@ -211,7 +210,7 @@ export const getSearchControlFactory = ( isClearable={false} // this will be handled by the clear floating action instead value={currentSearch ?? ''} onChange={(event) => { - searchString.next(event.target.value); + selections.setSearchString(event.target.value); }} placeholder={i18n.translate('controls.searchControl.placeholder', { defaultMessage: 'Search...', diff --git a/examples/controls_example/public/react_controls/data_controls/search_control/search_control_selections.ts b/examples/controls_example/public/react_controls/data_controls/search_control/search_control_selections.ts new file mode 100644 index 0000000000000..b36ac8aa4304c --- /dev/null +++ b/examples/controls_example/public/react_controls/data_controls/search_control/search_control_selections.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 { BehaviorSubject } from 'rxjs'; +import { PublishingSubject, StateComparators } from '@kbn/presentation-publishing'; +import { SearchControlState } from './types'; + +export function initializeSearchControlSelections( + initialState: SearchControlState, + onSelectionChange: () => void +) { + const searchString$ = new BehaviorSubject<string | undefined>(initialState.searchString); + function setSearchString(next: string | undefined) { + if (searchString$.value !== next) { + searchString$.next(next); + onSelectionChange(); + } + } + + return { + comparators: { + searchString: [searchString$, setSearchString], + } as StateComparators<Pick<SearchControlState, 'searchString'>>, + hasInitialSelections: initialState.searchString?.length, + searchString$: searchString$ as PublishingSubject<string | undefined>, + setSearchString, + }; +} diff --git a/examples/controls_example/public/react_controls/data_controls/types.ts b/examples/controls_example/public/react_controls/data_controls/types.ts index db4cba8773232..39effdb015184 100644 --- a/examples/controls_example/public/react_controls/data_controls/types.ts +++ b/examples/controls_example/public/react_controls/data_controls/types.ts @@ -10,17 +10,16 @@ import { CoreStart } from '@kbn/core/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { DataViewField } from '@kbn/data-views-plugin/common'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { Filter } from '@kbn/es-query'; import { FieldFormatConvertFunction } from '@kbn/field-formats-plugin/common'; import { HasEditCapabilities, PublishesDataViews, - PublishesFilters, PublishesPanelTitle, PublishingSubject, } from '@kbn/presentation-publishing'; import { ControlGroupApi } from '../control_group/types'; import { ControlFactory, DefaultControlApi, DefaultControlState } from '../types'; +import { PublishesAsyncFilters } from './publishes_async_filters'; export type DataControlFieldFormatter = FieldFormatConvertFunction | ((toFormat: any) => string); @@ -34,9 +33,7 @@ export type DataControlApi = DefaultControlApi & HasEditCapabilities & PublishesDataViews & PublishesField & - PublishesFilters & { - setOutputFilter: (filter: Filter | undefined) => void; // a control should only ever output a **single** filter - }; + PublishesAsyncFilters; export interface CustomOptionsComponentProps< State extends DefaultDataControlState = DefaultDataControlState diff --git a/examples/esql_ast_inspector/public/app.tsx b/examples/esql_ast_inspector/public/app.tsx index 4936544345f43..ac2a543bc40e5 100644 --- a/examples/esql_ast_inspector/public/app.tsx +++ b/examples/esql_ast_inspector/public/app.tsx @@ -18,6 +18,7 @@ import { EuiFormRow, EuiButton, } from '@elastic/eui'; +import { EuiProvider } from '@elastic/eui'; import type { CoreStart } from '@kbn/core/public'; @@ -42,48 +43,50 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => { }; return ( - <EuiPage> - <EuiPageBody style={{ maxWidth: 800, margin: '0 auto' }}> - <EuiPageHeader paddingSize="s" bottomBorder={true} pageTitle="ES|QL AST Inspector" /> - <EuiPageSection paddingSize="s"> - <p>This app gives you the AST for a particular ES|QL query.</p> + <EuiProvider> + <EuiPage> + <EuiPageBody style={{ maxWidth: 800, margin: '0 auto' }}> + <EuiPageHeader paddingSize="s" bottomBorder={true} pageTitle="ES|QL AST Inspector" /> + <EuiPageSection paddingSize="s"> + <p>This app gives you the AST for a particular ES|QL query.</p> - <EuiSpacer /> + <EuiSpacer /> - <EuiForm> - <EuiFormRow - fullWidth - label="Query" - isInvalid={Boolean(currentErrors.length)} - error={currentErrors.map((error) => error.message)} - > - <EuiTextArea - inputRef={(node) => { - inputRef.current = node; - }} - isInvalid={Boolean(currentErrors.length)} + <EuiForm> + <EuiFormRow fullWidth - value={currentQuery} - onChange={(e) => setQuery(e.target.value)} - css={{ - height: '5em', - }} - /> - </EuiFormRow> - <EuiFormRow fullWidth> - <EuiButton fullWidth onClick={() => parseQuery(inputRef.current?.value ?? '')}> - Parse - </EuiButton> - </EuiFormRow> - </EuiForm> - <EuiSpacer /> - <CodeEditor - allowFullScreen={true} - languageId={'json'} - value={JSON.stringify(ast, null, 2)} - /> - </EuiPageSection> - </EuiPageBody> - </EuiPage> + label="Query" + isInvalid={Boolean(currentErrors.length)} + error={currentErrors.map((error) => error.message)} + > + <EuiTextArea + inputRef={(node) => { + inputRef.current = node; + }} + isInvalid={Boolean(currentErrors.length)} + fullWidth + value={currentQuery} + onChange={(e) => setQuery(e.target.value)} + css={{ + height: '5em', + }} + /> + </EuiFormRow> + <EuiFormRow fullWidth> + <EuiButton fullWidth onClick={() => parseQuery(inputRef.current?.value ?? '')}> + Parse + </EuiButton> + </EuiFormRow> + </EuiForm> + <EuiSpacer /> + <CodeEditor + allowFullScreen={true} + languageId={'json'} + value={JSON.stringify(ast, null, 2)} + /> + </EuiPageSection> + </EuiPageBody> + </EuiPage> + </EuiProvider> ); }; diff --git a/examples/esql_validation_example/public/app.tsx b/examples/esql_validation_example/public/app.tsx index f785b31de3ba2..62b6405678d15 100644 --- a/examples/esql_validation_example/public/app.tsx +++ b/examples/esql_validation_example/public/app.tsx @@ -23,7 +23,7 @@ import { import type { CoreStart } from '@kbn/core/public'; -import { ESQLCallbacks, validateQuery } from '@kbn/esql-validation-autocomplete'; +import { ESQLCallbacks, ESQLRealField, validateQuery } from '@kbn/esql-validation-autocomplete'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; import type { StartDependencies } from './plugin'; import { CodeSnippet } from './code_snippet'; @@ -52,10 +52,11 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => { ['index1', 'anotherIndex', 'dataStream'].map((name) => ({ name, hidden: false })) : undefined, getFieldsFor: callbacksEnabled.fields - ? async () => [ - { name: 'numberField', type: 'number' }, - { name: 'stringField', type: 'string' }, - ] + ? async () => + [ + { name: 'doubleField', type: 'double' }, + { name: 'keywordField', type: 'keyword' }, + ] as ESQLRealField[] : undefined, getPolicies: callbacksEnabled.policies ? async () => [ diff --git a/fleet_packages.json b/fleet_packages.json index d0ee7ad089924..1217c4f2ec861 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -30,7 +30,7 @@ }, { "name": "elastic_agent", - "version": "2.0.1" + "version": "2.0.2" }, { "name": "endpoint", @@ -56,6 +56,6 @@ }, { "name": "security_detection_engine", - "version": "8.15.1" + "version": "8.15.2" } ] \ No newline at end of file diff --git a/oas_docs/makefile b/oas_docs/makefile index ec99abaf9e40d..6943921b1c6fe 100644 --- a/oas_docs/makefile +++ b/oas_docs/makefile @@ -15,7 +15,7 @@ .PHONY: api-docs api-docs: ## Generate kibana.serverless.yaml and kibana.yaml - @npx @redocly/cli join "kibana.info.serverless.yaml" "../x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml" "../x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml" "../src/plugins/data_views/docs/openapi/bundled.yaml" "../x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml" "../packages/core/saved-objects/docs/openapi/bundled_serverless.yaml" "../x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml" -o "output/kibana.serverless.yaml" "bundle.serverless.json" --prefix-components-with-info-prop title + @npx @redocly/cli join "kibana.info.serverless.yaml" "../x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml" "../x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml" "../src/plugins/data_views/docs/openapi/bundled.yaml" "../x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml" "../packages/core/saved-objects/docs/openapi/bundled_serverless.yaml" "../x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml" "bundle.serverless.json" -o "output/kibana.serverless.yaml" --prefix-components-with-info-prop title @npx @redocly/cli join "kibana.info.yaml" "../x-pack/plugins/alerting/docs/openapi/bundled.yaml" "../x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml" "../x-pack/plugins/cases/docs/openapi/bundled.yaml" "../x-pack/plugins/actions/docs/openapi/bundled.yaml" "../src/plugins/data_views/docs/openapi/bundled.yaml" "../x-pack/plugins/ml/common/openapi/ml_apis.yaml" "../packages/core/saved-objects/docs/openapi/bundled.yaml" "bundle.json" -o "output/kibana.yaml" --prefix-components-with-info-prop title .PHONY: api-docs-stateful @@ -25,7 +25,7 @@ api-docs-stateful: ## Generate only kibana.yaml .PHONY: api-docs-serverless api-docs-serverless: ## Generate only kibana.serverless.yaml - @npx @redocly/cli join "kibana.info.serverless.yaml" "../x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml" "../x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml" "../src/plugins/data_views/docs/openapi/bundled.yaml" "../x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml" "../packages/core/saved-objects/docs/openapi/bundled_serverless.yaml" "../x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml" -o "output/kibana.serverless.yaml" "bundle.serverless.json" --prefix-components-with-info-prop title + @npx @redocly/cli join "kibana.info.serverless.yaml" "../x-pack/plugins/observability_solution/apm/docs/openapi/apm.yaml" "../x-pack/plugins/actions/docs/openapi/bundled_serverless.yaml" "../src/plugins/data_views/docs/openapi/bundled.yaml" "../x-pack/plugins/ml/common/openapi/ml_apis_serverless.yaml" "../packages/core/saved-objects/docs/openapi/bundled_serverless.yaml" "../x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml" "bundle.serverless.json" -o "output/kibana.serverless.yaml" --prefix-components-with-info-prop title .PHONY: api-docs-lint api-docs-lint: ## Run spectral API docs linter diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 551ab3149dda9..a16a834c63e73 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -57,8 +57,6 @@ servers: variables: kibana_url: default: localhost:5601 - - url: http://localhost:5601 - description: local - url: http://localhost:5622 tags: - name: alerting @@ -94,9 +92,6 @@ tags: Manage Kibana saved objects, including dashboards, visualizations, and more. x-displayName: saved objects - - name: slo - description: SLO APIs enable you to define, manage and track service-level objectives - x-displayName: slo - name: system x-displayName: system paths: @@ -5497,453 +5492,6 @@ paths: application/json: schema: $ref: '#/components/schemas/Saved_objects_400_response' - /s/{spaceId}/api/observability/slos: - post: - summary: Create an SLO - operationId: createSloOp - description: > - You must have `all` privileges for the **SLOs** feature in the - **Observability** section of the Kibana feature privileges. - tags: - - slo - parameters: - - $ref: '#/components/parameters/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_create_slo_request' - responses: - '200': - description: Successful request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_create_slo_response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '409': - description: Conflict - The SLO id already exists - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_409_response' - servers: - - url: https://localhost:5601 - get: - summary: Get a paginated list of SLOs - operationId: findSlosOp - 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/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - - name: kqlQuery - in: query - description: A valid kql query to filter the SLO with - schema: - type: string - example: 'slo.name:latency* and slo.tags : "prod"' - - name: page - in: query - 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: Number of SLOs returned by page - schema: - type: integer - default: 25 - maximum: 5000 - example: 25 - - name: sortBy - in: query - description: Sort by field - schema: - type: string - enum: - - sli_value - - status - - error_budget_consumed - - error_budget_remaining - default: status - example: status - - name: sortDirection - in: query - description: Sort order - schema: - type: string - enum: - - asc - - desc - default: asc - example: asc - - name: hideStale - in: query - description: >- - Hide stale SLOs from the list as defined by stale SLO threshold in - SLO settings - schema: - type: boolean - responses: - '200': - description: Successful request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_find_slo_response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '404': - description: Not found response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_404_response' - /s/{spaceId}/api/observability/slos/{sloId}: - get: - summary: Get an SLO - operationId: getSloOp - 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/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - - $ref: '#/components/parameters/SLOs_slo_id' - - name: instanceId - in: query - description: the specific instanceId used by the summary calculation - schema: - type: string - example: host-abcde - responses: - '200': - description: Successful request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_slo_with_summary_response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '404': - description: Not found response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_404_response' - put: - summary: Update an SLO - operationId: updateSloOp - 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/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - - $ref: '#/components/parameters/SLOs_slo_id' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_update_slo_request' - responses: - '200': - description: Successful request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_slo_definition_response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '404': - description: Not found response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_404_response' - delete: - summary: Delete an SLO - operationId: deleteSloOp - 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/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - - $ref: '#/components/parameters/SLOs_slo_id' - responses: - '204': - description: Successful request - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '404': - description: Not found response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_404_response' - /s/{spaceId}/api/observability/slos/{sloId}/enable: - post: - summary: Enable an SLO - operationId: enableSloOp - 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/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - - $ref: '#/components/parameters/SLOs_slo_id' - responses: - '204': - description: Successful request - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '404': - description: Not found response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_404_response' - /s/{spaceId}/api/observability/slos/{sloId}/disable: - post: - summary: Disable an SLO - operationId: disableSloOp - 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/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - - $ref: '#/components/parameters/SLOs_slo_id' - responses: - '200': - description: Successful request - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '404': - description: Not found response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_404_response' - /s/{spaceId}/api/observability/slos/{sloId}/_reset: - post: - summary: Reset 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/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - - $ref: '#/components/parameters/SLOs_slo_id' - responses: - '204': - description: Successful request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_slo_definition_response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - '404': - description: Not found response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_404_response' - /s/{spaceId}/api/observability/slos/_delete_instances: - post: - summary: Batch delete rollup and summary data - operationId: deleteSloInstancesOp - description: > - The deletion occurs for the specified list of `sloId` and `instanceId`. - You must have `all` privileges for the **SLOs** feature in the - **Observability** section of the Kibana feature privileges. - tags: - - slo - parameters: - - $ref: '#/components/parameters/SLOs_kbn_xsrf' - - $ref: '#/components/parameters/SLOs_space_id' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_delete_slo_instances_request' - responses: - '204': - description: Successful request - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_400_response' - '401': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_401_response' - '403': - description: Unauthorized response - content: - application/json: - schema: - $ref: '#/components/schemas/SLOs_403_response' - servers: - - url: https://localhost:5601 /api/status: get: operationId: /api/status#0 @@ -6382,31 +5930,6 @@ components: required: true schema: type: string - SLOs_kbn_xsrf: - schema: - type: string - in: header - name: kbn-xsrf - description: Cross-site request forgery protection - required: true - SLOs_space_id: - in: path - name: spaceId - description: >- - An identifier for the space. If `/s/` and the identifier are omitted - from the path, the default space is used. - required: true - schema: - type: string - example: default - SLOs_slo_id: - in: path - name: sloId - description: An identifier for the slo. - required: true - schema: - type: string - example: 9c235211-6834-11ea-a78c-6feb38a34414 schemas: Alerting_create_anomaly_detection_alert_rule_request: title: Create anomaly detection rule request @@ -16367,1332 +15890,144 @@ components: data-frame-analytics: type: object description: >- - If saved objects are missing for data frame analytics jobs, they are - created. - additionalProperties: - $ref: >- - #/components/schemas/Machine_learning_APIs_mlSyncResponseDataFrameAnalytics - trained-model: - type: object - description: If saved objects are missing for trained models, they are created. - additionalProperties: - $ref: >- - #/components/schemas/Machine_learning_APIs_mlSyncResponseTrainedModels - Machine_learning_APIs_mlSyncResponseSavedObjectsDeleted: - type: object - title: Sync API response for deleted saved objects - description: >- - If saved objects exist for machine learning jobs or trained models that - no longer exist, they are deleted when you run the sync machine learning - saved objects API. - properties: - anomaly-detector: - type: object - description: >- - If there are saved objects exist for nonexistent anomaly detection - jobs, they are deleted. - additionalProperties: - $ref: >- - #/components/schemas/Machine_learning_APIs_mlSyncResponseAnomalyDetectors - data-frame-analytics: - type: object - description: >- - If there are saved objects exist for nonexistent data frame - analytics jobs, they are deleted. - additionalProperties: - $ref: >- - #/components/schemas/Machine_learning_APIs_mlSyncResponseDataFrameAnalytics - trained-model: - type: object - description: >- - If there are saved objects exist for nonexistent trained models, - they are deleted. - additionalProperties: - $ref: >- - #/components/schemas/Machine_learning_APIs_mlSyncResponseTrainedModels - Machine_learning_APIs_mlSyncResponseTrainedModels: - type: object - title: Sync API response for trained models - description: >- - The sync machine learning saved objects API response contains this - object when there are trained models affected by the synchronization. - There is an object for each relevant trained model, which contains the - synchronization status. - properties: - success: - $ref: '#/components/schemas/Machine_learning_APIs_mlSyncResponseSuccess' - Machine_learning_APIs_mlSync200Response: - type: object - title: Successful sync API response - properties: - datafeedsAdded: - type: object - description: >- - If a saved object for an anomaly detection job is missing a datafeed - identifier, it is added when you run the sync machine learning saved - objects API. - additionalProperties: - $ref: '#/components/schemas/Machine_learning_APIs_mlSyncResponseDatafeeds' - datafeedsRemoved: - type: object - description: >- - If a saved object for an anomaly detection job references a datafeed - that no longer exists, it is deleted when you run the sync machine - learning saved objects API. - additionalProperties: - $ref: '#/components/schemas/Machine_learning_APIs_mlSyncResponseDatafeeds' - savedObjectsCreated: - $ref: >- - #/components/schemas/Machine_learning_APIs_mlSyncResponseSavedObjectsCreated - savedObjectsDeleted: - $ref: >- - #/components/schemas/Machine_learning_APIs_mlSyncResponseSavedObjectsDeleted - Machine_learning_APIs_mlSync4xxResponse: - type: object - title: Unsuccessful sync API response - properties: - error: - type: string - example: Unauthorized - message: - type: string - statusCode: - type: integer - example: 401 - Saved_objects_400_response: - title: Bad request - type: object - required: - - error - - message - - statusCode - properties: - error: - type: string - enum: - - Bad Request - message: - type: string - statusCode: - type: integer - enum: - - 400 - Saved_objects_attributes: - type: object - description: > - The data that you want to create. WARNING: When you create saved - objects, attributes are not validated, which allows you to pass - arbitrary and ill-formed data into the API that can break Kibana. Make - sure any data that you send to the API is properly formed. - Saved_objects_initial_namespaces: - type: array - description: > - Identifiers for the spaces in which this object is created. If this is - provided, the object is created only in the explicitly defined spaces. - If this is not provided, the object is created in the current space - (default behavior). For shareable object types (registered with - `namespaceType: 'multiple'`), this option can be used to specify one or - more spaces, including the "All spaces" identifier ('*'). For isolated - object types (registered with `namespaceType: 'single'` or - `namespaceType: 'multiple-isolated'`), this option can only be used to - specify a single space, and the "All spaces" identifier ('*') is not - allowed. For global object types (`registered with `namespaceType: - agnostic`), this option cannot be used. - Saved_objects_references: - type: array - description: > - Objects with `name`, `id`, and `type` properties that describe the other - saved objects that this object references. Use `name` in attributes to - refer to the other saved object, but never the `id`, which can update - automatically during migrations or import and export. - SLOs_indicator_properties_apm_availability: - title: APM availability - required: - - type - - params - description: Defines properties for the APM availability indicator type - type: object - properties: - params: - description: An object containing the indicator parameters. - type: object - nullable: false - required: - - service - - environment - - transactionType - - transactionName - - index - properties: - service: - description: The APM service name - type: string - example: o11y-app - environment: - description: The APM service environment or "*" - type: string - example: production - transactionType: - description: The APM transaction type or "*" - type: string - example: request - transactionName: - description: The APM transaction name or "*" - type: string - example: GET /my/api - filter: - description: KQL query used for filtering the data - type: string - example: 'service.foo : "bar"' - index: - description: The index used by APM metrics - type: string - example: metrics-apm*,apm* - type: - description: The type of indicator. - type: string - example: sli.apm.transactionDuration - SLOs_filter_meta: - title: FilterMeta - description: Defines properties for a filter - type: object - properties: - alias: - type: string - nullable: true - disabled: - type: boolean - negate: - type: boolean - controlledBy: - type: string - group: - type: string - index: - type: string - isMultiIndex: - type: boolean - type: - type: string - key: - type: string - params: - type: object - value: - type: string - field: - type: string - SLOs_filter: - title: Filter - description: Defines properties for a filter - type: object - properties: - query: - type: object - meta: - $ref: '#/components/schemas/SLOs_filter_meta' - SLOs_kql_with_filters: - title: KQL with filters - description: Defines properties for a filter - oneOf: - - description: the KQL query to filter the documents with. - type: string - example: 'field.environment : "production" and service.name : "my-service"' - - type: object - properties: - kqlQuery: - type: string - filters: - type: array - items: - $ref: '#/components/schemas/SLOs_filter' - SLOs_kql_with_filters_good: - title: KQL query for good events - description: The KQL query used to define the good events. - oneOf: - - description: the KQL query to filter the documents with. - type: string - example: 'request.latency <= 150 and request.status_code : "2xx"' - - type: object - properties: - kqlQuery: - type: string - filters: - type: array - items: - $ref: '#/components/schemas/SLOs_filter' - SLOs_kql_with_filters_total: - title: KQL query for all events - description: The KQL query used to define all events. - oneOf: - - description: the KQL query to filter the documents with. - type: string - example: 'field.environment : "production" and service.name : "my-service"' - - type: object - properties: - kqlQuery: - type: string - filters: - type: array - items: - $ref: '#/components/schemas/SLOs_filter' - SLOs_indicator_properties_custom_kql: - title: Custom Query - required: - - type - - params - description: Defines properties for a custom query indicator type - type: object - properties: - params: - description: An object containing the indicator parameters. - type: object - nullable: false - required: - - index - - timestampField - - good - - total - properties: - index: - description: The index or index pattern to use - type: string - example: my-service-* - dataViewId: - description: >- - The kibana data view id to use, primarily used to include data - view runtime mappings. Make sure to save SLO again if you - add/update run time fields to the data view and if those fields - are being used in slo queries. - type: string - example: 03b80ab3-003d-498b-881c-3beedbaf1162 - filter: - $ref: '#/components/schemas/SLOs_kql_with_filters' - good: - $ref: '#/components/schemas/SLOs_kql_with_filters_good' - total: - $ref: '#/components/schemas/SLOs_kql_with_filters_total' - timestampField: - description: | - The timestamp field used in the source indice. - type: string - example: timestamp - type: - description: The type of indicator. - type: string - example: sli.kql.custom - SLOs_indicator_properties_apm_latency: - title: APM latency - required: - - type - - params - description: Defines properties for the APM latency indicator type - type: object - properties: - params: - description: An object containing the indicator parameters. - type: object - nullable: false - required: - - service - - environment - - transactionType - - transactionName - - index - - threshold - properties: - service: - description: The APM service name - type: string - example: o11y-app - environment: - description: The APM service environment or "*" - type: string - example: production - transactionType: - description: The APM transaction type or "*" - type: string - example: request - transactionName: - description: The APM transaction name or "*" - type: string - example: GET /my/api - filter: - description: KQL query used for filtering the data - type: string - example: 'service.foo : "bar"' - index: - description: The index used by APM metrics - type: string - example: metrics-apm*,apm* - threshold: - description: The latency threshold in milliseconds - type: number - example: 250 - type: - description: The type of indicator. - type: string - example: sli.apm.transactionDuration - SLOs_indicator_properties_custom_metric: - title: Custom metric - required: - - type - - params - description: Defines properties for a custom metric indicator type - type: object - properties: - params: - description: An object containing the indicator parameters. - type: object - nullable: false - required: - - index - - timestampField - - good - - total - properties: - index: - description: The index or index pattern to use - type: string - example: my-service-* - dataViewId: - description: >- - The kibana data view id to use, primarily used to include data - view runtime mappings. Make sure to save SLO again if you - add/update run time fields to the data view and if those fields - are being used in slo queries. - type: string - example: 03b80ab3-003d-498b-881c-3beedbaf1162 - filter: - description: the KQL query to filter the documents with. - type: string - example: 'field.environment : "production" and service.name : "my-service"' - timestampField: - description: | - The timestamp field used in the source indice. - type: string - example: timestamp - good: - description: | - An object defining the "good" metrics and equation - type: object - required: - - metrics - - equation - properties: - metrics: - description: >- - List of metrics with their name, aggregation type, and - field. - type: array - items: - type: object - required: - - name - - aggregation - - field - properties: - name: - description: The name of the metric. Only valid options are A-Z - type: string - example: A - pattern: ^[A-Z]$ - aggregation: - description: >- - The aggregation type of the metric. Only valid option - is "sum" - type: string - example: sum - enum: - - sum - field: - description: The field of the metric. - type: string - example: processor.processed - filter: - description: The filter to apply to the metric. - type: string - example: 'processor.outcome: "success"' - equation: - description: The equation to calculate the "good" metric. - type: string - example: A - total: - description: | - An object defining the "total" metrics and equation - type: object - required: - - metrics - - equation - properties: - metrics: - description: >- - List of metrics with their name, aggregation type, and - field. - type: array - items: - type: object - required: - - name - - aggregation - - field - properties: - name: - description: The name of the metric. Only valid options are A-Z - type: string - example: A - pattern: ^[A-Z]$ - aggregation: - description: >- - The aggregation type of the metric. Only valid option - is "sum" - type: string - example: sum - enum: - - sum - field: - description: The field of the metric. - type: string - example: processor.processed - filter: - description: The filter to apply to the metric. - type: string - example: 'processor.outcome: *' - equation: - description: The equation to calculate the "total" metric. - type: string - example: A - type: - description: The type of indicator. - type: string - example: sli.metric.custom - SLOs_indicator_properties_histogram: - title: Histogram indicator - required: - - type - - params - description: Defines properties for a histogram indicator type - type: object - properties: - params: - description: An object containing the indicator parameters. - type: object - nullable: false - required: - - index - - timestampField - - good - - total - properties: - index: - description: The index or index pattern to use - type: string - example: my-service-* - dataViewId: - description: >- - The kibana data view id to use, primarily used to include data - view runtime mappings. Make sure to save SLO again if you - add/update run time fields to the data view and if those fields - are being used in slo queries. - type: string - example: 03b80ab3-003d-498b-881c-3beedbaf1162 - filter: - description: the KQL query to filter the documents with. - type: string - example: 'field.environment : "production" and service.name : "my-service"' - timestampField: - description: | - The timestamp field used in the source indice. - type: string - example: timestamp - good: - description: | - An object defining the "good" events - type: object - required: - - aggregation - - field - properties: - field: - description: The field use to aggregate the good events. - type: string - example: processor.latency - aggregation: - description: The type of aggregation to use. - type: string - example: value_count - enum: - - value_count - - range - filter: - description: The filter for good events. - type: string - example: 'processor.outcome: "success"' - from: - description: >- - The starting value of the range. Only required for "range" - aggregations. - type: number - example: 0 - to: - description: >- - The ending value of the range. Only required for "range" - aggregations. - type: number - example: 100 - total: - description: | - An object defining the "total" events - type: object - required: - - aggregation - - field - properties: - field: - description: The field use to aggregate the good events. - type: string - example: processor.latency - aggregation: - description: The type of aggregation to use. - type: string - example: value_count - enum: - - value_count - - range - filter: - description: The filter for total events. - type: string - example: 'processor.outcome : *' - from: - description: >- - The starting value of the range. Only required for "range" - aggregations. - type: number - example: 0 - to: - description: >- - The ending value of the range. Only required for "range" - aggregations. - type: number - example: 100 - type: - description: The type of indicator. - type: string - example: sli.histogram.custom - SLOs_timeslice_metric_basic_metric_with_field: - title: Timeslice Metric Basic Metric with Field - required: - - name - - aggregation - - field - type: object - properties: - name: - description: The name of the metric. Only valid options are A-Z - type: string - example: A - pattern: ^[A-Z]$ - aggregation: - description: The aggregation type of the metric. - type: string - example: sum - enum: - - sum - - avg - - min - - max - - std_deviation - - last_value - - cardinality - field: - description: The field of the metric. - type: string - example: processor.processed - filter: - description: The filter to apply to the metric. - type: string - example: 'processor.outcome: "success"' - SLOs_timeslice_metric_percentile_metric: - title: Timeslice Metric Percentile Metric - required: - - name - - aggregation - - field - - percentile - type: object - properties: - name: - description: The name of the metric. Only valid options are A-Z - type: string - example: A - pattern: ^[A-Z]$ - aggregation: - description: >- - The aggregation type of the metric. Only valid option is - "percentile" - type: string - example: percentile - enum: - - percentile - field: - description: The field of the metric. - type: string - example: processor.processed - percentile: - description: The percentile value. - type: number - example: 95 - filter: - description: The filter to apply to the metric. - type: string - example: 'processor.outcome: "success"' - SLOs_timeslice_metric_doc_count_metric: - title: Timeslice Metric Doc Count Metric - required: - - name - - aggregation - type: object - properties: - name: - description: The name of the metric. Only valid options are A-Z - type: string - example: A - pattern: ^[A-Z]$ - aggregation: - description: The aggregation type of the metric. Only valid option is "doc_count" - type: string - example: doc_count - enum: - - doc_count - filter: - description: The filter to apply to the metric. - type: string - example: 'processor.outcome: "success"' - SLOs_indicator_properties_timeslice_metric: - title: Timeslice metric - required: - - type - - params - description: Defines properties for a timeslice metric indicator type - type: object - properties: - params: - description: An object containing the indicator parameters. - type: object - nullable: false - required: - - index - - timestampField - - metric - properties: - index: - description: The index or index pattern to use - type: string - example: my-service-* - dataViewId: - description: >- - The kibana data view id to use, primarily used to include data - view runtime mappings. Make sure to save SLO again if you - add/update run time fields to the data view and if those fields - are being used in slo queries. - type: string - example: 03b80ab3-003d-498b-881c-3beedbaf1162 - filter: - description: the KQL query to filter the documents with. - type: string - example: 'field.environment : "production" and service.name : "my-service"' - timestampField: - description: | - The timestamp field used in the source indice. - type: string - example: timestamp - metric: - description: > - An object defining the metrics, equation, and threshold to - determine if it's a good slice or not - type: object - required: - - metrics - - equation - - comparator - - threshold - properties: - metrics: - description: >- - List of metrics with their name, aggregation type, and - field. - type: array - items: - anyOf: - - $ref: >- - #/components/schemas/SLOs_timeslice_metric_basic_metric_with_field - - $ref: >- - #/components/schemas/SLOs_timeslice_metric_percentile_metric - - $ref: >- - #/components/schemas/SLOs_timeslice_metric_doc_count_metric - equation: - description: The equation to calculate the metric. - type: string - example: A - comparator: - description: >- - The comparator to use to compare the equation to the - threshold. - type: string - example: GT - enum: - - GT - - GTE - - LT - - LTE - threshold: - description: >- - The threshold used to determine if the metric is a good - slice or not. - type: number - example: 100 - type: - description: The type of indicator. - type: string - example: sli.metric.timeslice - SLOs_time_window: - title: Time window - required: - - duration - - type - description: Defines properties for the SLO time window - type: object - properties: - duration: - description: >- - the duration formatted as {duration}{unit}. Accepted values for - rolling: 7d, 30d, 90d. Accepted values for calendar aligned: 1w - (weekly) or 1M (monthly) - type: string - example: 30d - type: - description: >- - Indicates weither the time window is a rolling or a calendar aligned - time window. - type: string - example: rolling - enum: - - rolling - - calendarAligned - SLOs_budgeting_method: - title: Budgeting method - type: string - description: The budgeting method to use when computing the rollup data. - enum: - - occurrences - - timeslices - example: occurrences - SLOs_objective: - title: Objective - required: - - target - description: Defines properties for the SLO objective - type: object - properties: - target: - description: the target objective between 0 and 1 excluded - type: number - minimum: 0 - maximum: 100 - exclusiveMinimum: true - exclusiveMaximum: true - example: 0.99 - timesliceTarget: - description: >- - the target objective for each slice when using a timeslices - budgeting method - type: number - minimum: 0 - maximum: 100 - example: 0.995 - timesliceWindow: - description: >- - the duration of each slice when using a timeslices budgeting method, - as {duraton}{unit} - type: string - example: 5m - SLOs_settings: - title: Settings - description: Defines properties for SLO settings. - type: object - properties: - syncDelay: - description: The synch delay to apply to the transform. Default 1m - type: string - default: 1m - example: 5m - frequency: - description: Configure how often the transform runs, default 1m - type: string - default: 1m - example: 5m - preventInitialBackfill: - description: Prevents the transform from backfilling data when it starts. - type: boolean - default: false - example: true - SLOs_summary_status: - title: summary status - type: string - enum: - - NO_DATA - - HEALTHY - - DEGRADING - - VIOLATED - example: HEALTHY - SLOs_error_budget: - title: Error budget - type: object - required: - - initial - - consumed - - remaining - - isEstimated - properties: - initial: - type: number - description: The initial error budget, as 1 - objective - example: 0.02 - consumed: - type: number - description: The error budget consummed, as a percentage of the initial value. - example: 0.8 - remaining: - type: number - description: The error budget remaining, as a percentage of the initial value. - example: 0.2 - isEstimated: - type: boolean - description: >- - Only for SLO defined with occurrences budgeting method and calendar - aligned time window. - example: true - SLOs_summary: - title: Summary - type: object - description: The SLO computed data - required: - - status - - sliValue - - errorBudget - properties: - status: - $ref: '#/components/schemas/SLOs_summary_status' - sliValue: - type: number - example: 0.9836 - errorBudget: - $ref: '#/components/schemas/SLOs_error_budget' - SLOs_slo_with_summary_response: - title: SLO response - type: object - required: - - id - - name - - description - - indicator - - timeWindow - - budgetingMethod - - objective - - settings - - revision - - summary - - enabled - - groupBy - - instanceId - - 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/SLOs_indicator_properties_apm_availability' - sli.kql.custom: '#/components/schemas/SLOs_indicator_properties_custom_kql' - sli.apm.transactionDuration: '#/components/schemas/SLOs_indicator_properties_apm_latency' - sli.metric.custom: '#/components/schemas/SLOs_indicator_properties_custom_metric' - sli.histogram.custom: '#/components/schemas/SLOs_indicator_properties_histogram' - sli.metric.timeslice: '#/components/schemas/SLOs_indicator_properties_timeslice_metric' - oneOf: - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_kql' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_availability' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_latency' - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_metric' - - $ref: '#/components/schemas/SLOs_indicator_properties_histogram' - - $ref: '#/components/schemas/SLOs_indicator_properties_timeslice_metric' - timeWindow: - $ref: '#/components/schemas/SLOs_time_window' - budgetingMethod: - $ref: '#/components/schemas/SLOs_budgeting_method' - objective: - $ref: '#/components/schemas/SLOs_objective' - settings: - $ref: '#/components/schemas/SLOs_settings' - revision: - description: The SLO revision - type: number - example: 2 - summary: - $ref: '#/components/schemas/SLOs_summary' - 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 - instanceId: - description: the value derived from the groupBy field, if present, otherwise '*' - type: string - example: host-abcde - 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 - SLOs_find_slo_response: - title: Find SLO response - description: | - A paginated response of SLOs matching the query. + If saved objects are missing for data frame analytics jobs, they are + created. + additionalProperties: + $ref: >- + #/components/schemas/Machine_learning_APIs_mlSyncResponseDataFrameAnalytics + trained-model: + type: object + description: If saved objects are missing for trained models, they are created. + additionalProperties: + $ref: >- + #/components/schemas/Machine_learning_APIs_mlSyncResponseTrainedModels + Machine_learning_APIs_mlSyncResponseSavedObjectsDeleted: type: object + title: Sync API response for deleted saved objects + description: >- + If saved objects exist for machine learning jobs or trained models that + no longer exist, they are deleted when you run the sync machine learning + saved objects API. properties: - page: - type: number - example: 1 - perPage: - type: number - example: 25 - total: - type: number - example: 34 - results: - type: array - items: - $ref: '#/components/schemas/SLOs_slo_with_summary_response' - SLOs_400_response: - title: Bad request + anomaly-detector: + type: object + description: >- + If there are saved objects exist for nonexistent anomaly detection + jobs, they are deleted. + additionalProperties: + $ref: >- + #/components/schemas/Machine_learning_APIs_mlSyncResponseAnomalyDetectors + data-frame-analytics: + type: object + description: >- + If there are saved objects exist for nonexistent data frame + analytics jobs, they are deleted. + additionalProperties: + $ref: >- + #/components/schemas/Machine_learning_APIs_mlSyncResponseDataFrameAnalytics + trained-model: + type: object + description: >- + If there are saved objects exist for nonexistent trained models, + they are deleted. + additionalProperties: + $ref: >- + #/components/schemas/Machine_learning_APIs_mlSyncResponseTrainedModels + Machine_learning_APIs_mlSyncResponseTrainedModels: type: object - required: - - statusCode - - error - - message + title: Sync API response for trained models + description: >- + The sync machine learning saved objects API response contains this + object when there are trained models affected by the synchronization. + There is an object for each relevant trained model, which contains the + synchronization status. properties: - statusCode: - type: number - example: 400 - error: - type: string - example: Bad Request - message: - type: string - example: 'Invalid value ''foo'' supplied to: [...]' - SLOs_401_response: - title: Unauthorized + success: + $ref: '#/components/schemas/Machine_learning_APIs_mlSyncResponseSuccess' + Machine_learning_APIs_mlSync200Response: type: object - required: - - statusCode - - error - - message + title: Successful sync API response properties: - statusCode: - type: number - example: 401 - error: - type: string - example: Unauthorized - message: - type: string - example: "[security_exception\n\tRoot causes:\n\t\tsecurity_exception: unable to authenticate user [elastics] for REST request [/_security/_authenticate]]: unable to authenticate user [elastics] for REST request [/_security/_authenticate]" - SLOs_403_response: - title: Unauthorized + datafeedsAdded: + type: object + description: >- + If a saved object for an anomaly detection job is missing a datafeed + identifier, it is added when you run the sync machine learning saved + objects API. + additionalProperties: + $ref: '#/components/schemas/Machine_learning_APIs_mlSyncResponseDatafeeds' + datafeedsRemoved: + type: object + description: >- + If a saved object for an anomaly detection job references a datafeed + that no longer exists, it is deleted when you run the sync machine + learning saved objects API. + additionalProperties: + $ref: '#/components/schemas/Machine_learning_APIs_mlSyncResponseDatafeeds' + savedObjectsCreated: + $ref: >- + #/components/schemas/Machine_learning_APIs_mlSyncResponseSavedObjectsCreated + savedObjectsDeleted: + $ref: >- + #/components/schemas/Machine_learning_APIs_mlSyncResponseSavedObjectsDeleted + Machine_learning_APIs_mlSync4xxResponse: type: object - required: - - statusCode - - error - - message + title: Unsuccessful sync API response properties: - statusCode: - type: number - example: 403 error: type: string example: Unauthorized message: type: string - example: "[security_exception\n\tRoot causes:\n\t\tsecurity_exception: unable to authenticate user [elastics] for REST request [/_security/_authenticate]]: unable to authenticate user [elastics] for REST request [/_security/_authenticate]" - SLOs_404_response: - title: Not found - type: object - required: - - statusCode - - error - - message - properties: statusCode: - type: number - example: 404 - error: - type: string - example: Not Found - message: - type: string - example: SLO [3749f390-03a3-11ee-8139-c7ff60a1692d] not found - SLOs_create_slo_request: - title: Create SLO request - description: > - The create SLO API request body varies depending on the type of - indicator, time window and budgeting method. - type: object - required: - - name - - description - - indicator - - timeWindow - - budgetingMethod - - objective - properties: - id: - description: >- - A optional and unique identifier for the SLO. Must be between 8 and - 36 chars - type: string - example: my-super-slo-id - name: - description: A name for the SLO. - type: string - description: - description: A description for the SLO. - type: string - indicator: - oneOf: - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_kql' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_availability' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_latency' - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_metric' - - $ref: '#/components/schemas/SLOs_indicator_properties_histogram' - - $ref: '#/components/schemas/SLOs_indicator_properties_timeslice_metric' - timeWindow: - $ref: '#/components/schemas/SLOs_time_window' - budgetingMethod: - $ref: '#/components/schemas/SLOs_budgeting_method' - objective: - $ref: '#/components/schemas/SLOs_objective' - settings: - $ref: '#/components/schemas/SLOs_settings' - 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 - SLOs_create_slo_response: - title: Create SLO response - type: object - required: - - id - properties: - id: - type: string - example: 8853df00-ae2e-11ed-90af-09bb6422b258 - SLOs_409_response: - title: Conflict + type: integer + example: 401 + Saved_objects_400_response: + title: Bad request type: object required: - - statusCode - error - message + - statusCode properties: - statusCode: - type: number - example: 409 error: type: string - example: Conflict + enum: + - Bad Request message: type: string - example: SLO [d077e940-1515-11ee-9c50-9d096392f520] already exists - SLOs_update_slo_request: - title: Update SLO request - description: > - The update SLO API request body varies depending on the type of - indicator, time window and budgeting method. Partial update is handled. - type: object - properties: - name: - description: A name for the SLO. - type: string - description: - description: A description for the SLO. - type: string - indicator: - oneOf: - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_kql' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_availability' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_latency' - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_metric' - - $ref: '#/components/schemas/SLOs_indicator_properties_histogram' - - $ref: '#/components/schemas/SLOs_indicator_properties_timeslice_metric' - timeWindow: - $ref: '#/components/schemas/SLOs_time_window' - budgetingMethod: - $ref: '#/components/schemas/SLOs_budgeting_method' - objective: - $ref: '#/components/schemas/SLOs_objective' - settings: - $ref: '#/components/schemas/SLOs_settings' - tags: - description: List of tags - type: array - items: - type: string - SLOs_slo_definition_response: - title: SLO definition response + statusCode: + type: integer + enum: + - 400 + Saved_objects_attributes: 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/SLOs_indicator_properties_apm_availability' - sli.kql.custom: '#/components/schemas/SLOs_indicator_properties_custom_kql' - sli.apm.transactionDuration: '#/components/schemas/SLOs_indicator_properties_apm_latency' - sli.metric.custom: '#/components/schemas/SLOs_indicator_properties_custom_metric' - sli.histogram.custom: '#/components/schemas/SLOs_indicator_properties_histogram' - sli.metric.timeslice: '#/components/schemas/SLOs_indicator_properties_timeslice_metric' - oneOf: - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_kql' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_availability' - - $ref: '#/components/schemas/SLOs_indicator_properties_apm_latency' - - $ref: '#/components/schemas/SLOs_indicator_properties_custom_metric' - - $ref: '#/components/schemas/SLOs_indicator_properties_histogram' - - $ref: '#/components/schemas/SLOs_indicator_properties_timeslice_metric' - timeWindow: - $ref: '#/components/schemas/SLOs_time_window' - budgetingMethod: - $ref: '#/components/schemas/SLOs_budgeting_method' - objective: - $ref: '#/components/schemas/SLOs_objective' - settings: - $ref: '#/components/schemas/SLOs_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 - SLOs_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. - type: object - required: - - list - properties: - list: - description: An array of slo id and instance id - type: array - items: - type: object - required: - - sloId - - instanceId - properties: - sloId: - description: The SLO unique identifier - type: string - example: 8853df00-ae2e-11ed-90af-09bb6422b258 - instanceId: - description: The SLO instance identifier - type: string - example: 8853df00-ae2e-11ed-90af-09bb6422b258 + The data that you want to create. WARNING: When you create saved + objects, attributes are not validated, which allows you to pass + arbitrary and ill-formed data into the API that can break Kibana. Make + sure any data that you send to the API is properly formed. + Saved_objects_initial_namespaces: + type: array + description: > + Identifiers for the spaces in which this object is created. If this is + provided, the object is created only in the explicitly defined spaces. + If this is not provided, the object is created in the current space + (default behavior). For shareable object types (registered with + `namespaceType: 'multiple'`), this option can be used to specify one or + more spaces, including the "All spaces" identifier ('*'). For isolated + object types (registered with `namespaceType: 'single'` or + `namespaceType: 'multiple-isolated'`), this option can only be used to + specify a single space, and the "All spaces" identifier ('*') is not + allowed. For global object types (`registered with `namespaceType: + agnostic`), this option cannot be used. + Saved_objects_references: + type: array + description: > + Objects with `name`, `id`, and `type` properties that describe the other + saved objects that this object references. Use `name` in attributes to + refer to the other saved object, but never the `id`, which can update + automatically during migrations or import and export. Kibana_HTTP_APIs_core_status_redactedResponse: additionalProperties: false description: A minimal representation of Kibana's operational status. @@ -22131,9 +20466,6 @@ x-tagGroups: - name: Saved objects tags: - saved objects - - name: SLOs - tags: - - slo - name: Kibana HTTP APIs tags: - system diff --git a/oas_docs/overlays/kibana.overlays.serverless.yaml b/oas_docs/overlays/kibana.overlays.serverless.yaml index 86a73d57c8561..6ed2329c24dca 100644 --- a/oas_docs/overlays/kibana.overlays.serverless.yaml +++ b/oas_docs/overlays/kibana.overlays.serverless.yaml @@ -37,4 +37,37 @@ actions: - target: '$.paths[*][*]' description: Add x-beta update: - x-beta: true \ No newline at end of file + x-beta: true + # Add some tag descriptions and displayNames + - target: '$.tags[?(@.name=="connectors")]' + description: Change tag description and displayName + update: + description: > + Connectors provide a central place to store connection information for services and integrations with Elastic or third party systems. + Alerting rules can use connectors to run actions when rule conditions are met. + externalDocs: + description: Connector documentation + url: https://www.elastic.co/docs/current/serverless/action-connectors + x-displayName: "Connectors" + - target: '$.tags[?(@.name=="data views")]' + description: Change displayName + update: + x-displayName: "Data views" + - target: '$.tags[?(@.name=="ml")]' + description: Change displayName + update: + x-displayName: "Machine learning" + - target: '$.tags[?(@.name=="saved objects")]' + description: Change displayName + update: + x-displayName: "Saved objects" + - target: '$.tags[?(@.name=="slo")]' + description: Change displayName + update: + x-displayName: "Service level objectives" + - target: '$.tags[?(@.name=="system")]' + description: Change displayName + update: + x-displayName: "System" + description: > + Get information about the system status, resource usage, and installed plugins. \ No newline at end of file diff --git a/oas_docs/overlays/kibana.overlays.yaml b/oas_docs/overlays/kibana.overlays.yaml index d1acdf7712bf8..22162721c6867 100644 --- a/oas_docs/overlays/kibana.overlays.yaml +++ b/oas_docs/overlays/kibana.overlays.yaml @@ -60,3 +60,58 @@ actions: If you use the Kibana console to send API requests, it automatically adds the appropriate space identifier. To learn more, check out [Spaces](https://www.elastic.co/guide/en/kibana/current/xpack-spaces.html). + # Add some tag descriptions and displayNames + - target: '$.tags[?(@.name=="alerting")]' + description: Change tag description and displayName + update: + description: > + Alerting enables you to define rules, which detect complex conditions within your data. + When a condition is met, the rule tracks it as an alert and runs the actions that are defined in the rule. + Actions typically involve the use of connectors to interact with Kibana services or third party integrations. + externalDocs: + description: Alerting documentation + url: https://www.elastic.co/guide/en/kibana/current/alerting-getting-started.html + x-displayName: "Alerting" + - target: '$.tags[?(@.name=="cases")]' + description: Change tag description and displayName + update: + description: > + Cases are used to open and track issues. + You can add assignees and tags to your cases, set their severity and status, and add alerts, comments, and visualizations. + You can also send cases to external incident management systems by configuring connectors. + externalDocs: + description: Cases documentation + url: https://www.elastic.co/guide/en/kibana/current/cases.html + x-displayName: "Cases" + - target: '$.tags[?(@.name=="connectors")]' + description: Change tag description and displayName + update: + description: > + Connectors provide a central place to store connection information for services and integrations with Elastic or third party systems. + Alerting rules can use connectors to run actions when rule conditions are met. + externalDocs: + description: Connector documentation + url: https://www.elastic.co/guide/en/kibana/current/action-types.html + x-displayName: "Connectors" + - target: '$.tags[?(@.name=="data views")]' + description: Change displayName + update: + x-displayName: "Data views" + - target: '$.tags[?(@.name=="ml")]' + description: Change displayName + update: + x-displayName: "Machine learning" + - target: '$.tags[?(@.name=="saved objects")]' + description: Change displayName + update: + x-displayName: "Saved objects" + # - target: '$.tags[?(@.name=="slo")]' + # description: Change displayName + # update: + # x-displayName: "Service level objectives" + - target: '$.tags[?(@.name=="system")]' + description: Change displayName + update: + x-displayName: "System" + description: > + Get information about the system status, resource usage, and installed plugins. diff --git a/package.json b/package.json index 655bca42fc19e..c287872dd5788 100644 --- a/package.json +++ b/package.json @@ -185,7 +185,6 @@ "@kbn/apm-utils": "link:packages/kbn-apm-utils", "@kbn/app-link-test-plugin": "link:test/plugin_functional/plugins/app_link_test", "@kbn/application-usage-test-plugin": "link:x-pack/test/usage_collection/plugins/application_usage_test", - "@kbn/assets-data-access-plugin": "link:x-pack/plugins/observability_solution/assets_data_access", "@kbn/audit-log-plugin": "link:x-pack/test/security_api_integration/plugins/audit_log", "@kbn/avc-banner": "link:packages/kbn-avc-banner", "@kbn/banners-plugin": "link:x-pack/plugins/banners", @@ -453,6 +452,7 @@ "@kbn/embedded-lens-example-plugin": "link:x-pack/examples/embedded_lens_example", "@kbn/encrypted-saved-objects-plugin": "link:x-pack/plugins/encrypted_saved_objects", "@kbn/enterprise-search-plugin": "link:x-pack/plugins/enterprise_search", + "@kbn/entities-data-access-plugin": "link:x-pack/plugins/observability_solution/entities_data_access", "@kbn/entities-schema": "link:x-pack/packages/kbn-entities-schema", "@kbn/entityManager-plugin": "link:x-pack/plugins/observability_solution/entity_manager", "@kbn/error-boundary-example-plugin": "link:examples/error_boundary", @@ -956,7 +956,7 @@ "@langchain/langgraph": "^0.0.31", "@langchain/openai": "^0.1.3", "@langtrase/trace-attributes": "^3.0.8", - "@launchdarkly/node-server-sdk": "^9.4.7", + "@launchdarkly/node-server-sdk": "^9.5.0", "@loaders.gl/core": "^3.4.7", "@loaders.gl/json": "^3.4.7", "@loaders.gl/shapefile": "^3.4.7", @@ -1150,7 +1150,7 @@ "re2js": "0.4.1", "react": "^17.0.2", "react-ace": "^7.0.5", - "react-diff-view": "^3.2.0", + "react-diff-view": "^3.2.1", "react-dom": "^17.0.2", "react-dropzone": "^4.2.9", "react-fast-compare": "^2.0.4", @@ -1432,7 +1432,7 @@ "@mapbox/vector-tile": "1.3.1", "@octokit/rest": "^17.11.2", "@parcel/watcher": "^2.1.0", - "@redocly/cli": "^1.18.1", + "@redocly/cli": "^1.19.0", "@statoscope/webpack-plugin": "^5.28.2", "@storybook/addon-a11y": "^6.5.16", "@storybook/addon-actions": "^6.5.16", diff --git a/packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts b/packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts new file mode 100644 index 0000000000000..06b349d4c6043 --- /dev/null +++ b/packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.test.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { trackPerformanceMeasureEntries } from './track_performance_measure_entries'; +import { analyticsClientMock } from './analytics_service.test.mocks'; + +interface MockEntryList { + getEntries: () => [object]; +} +type ObsCallback = (_entries: MockEntryList, _obs: object) => undefined; +const mockObs = { observe: jest.fn, disconnect: jest.fn }; + +const setupMockPerformanceObserver = (entries: [object]) => { + const mockPerformanceObserver = function (callback: ObsCallback) { + callback( + { + getEntries: () => entries, + }, + mockObs + ); + return mockObs; + }; + + (global.PerformanceObserver as unknown) = mockPerformanceObserver; +}; + +describe('trackPerformanceMeasureEntries', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test("doesn't report an analytics event when not receiving events", () => { + setupMockPerformanceObserver([{}]); + trackPerformanceMeasureEntries(analyticsClientMock, true); + + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(0); + }); + + test("doesn't report an analytics event when receiving not 'kibana:performance' events", () => { + setupMockPerformanceObserver([ + { + name: '/', + entryType: 'measure', + startTime: 100, + duration: 1000, + detail: { + eventName: 'kibana:plugin_render_time', + type: 'anything', + }, + }, + ]); + trackPerformanceMeasureEntries(analyticsClientMock, true); + + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(0); + }); + + test("doesn't report an analytics event when receiving not 'measure' events", () => { + setupMockPerformanceObserver([ + { + name: '/', + entryType: 'anything', + startTime: 100, + duration: 1000, + detail: { + eventName: 'kibana:plugin_render_time', + type: 'kibana:performance', + }, + }, + ]); + trackPerformanceMeasureEntries(analyticsClientMock, true); + + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(0); + }); + + test('reports an analytics event when receiving "measure" and "kibana:performance" events', () => { + setupMockPerformanceObserver([ + { + name: '/', + entryType: 'measure', + startTime: 100, + duration: 1000, + detail: { + eventName: 'kibana:plugin_render_time', + type: 'kibana:performance', + }, + }, + ]); + trackPerformanceMeasureEntries(analyticsClientMock, true); + + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(1); + }); + + test('reports an analytics event ignoring keys and values not allowed', () => { + setupMockPerformanceObserver([ + { + name: '/', + entryType: 'measure', + startTime: 100, + duration: 1000, + detail: { + eventName: 'kibana:plugin_render_time', + type: 'kibana:performance', + customMetrics: { + key1: 'key1', + value1: 'value1', + key10: 'key10', + value10: 'value10', + anyKey: 'anyKey', + anyValue: 'anyValue', + }, + }, + }, + ]); + trackPerformanceMeasureEntries(analyticsClientMock, true); + + expect(analyticsClientMock.reportEvent).toHaveBeenCalledTimes(1); + expect(analyticsClientMock.reportEvent).toHaveBeenCalledWith('performance_metric', { + duration: 1000, + eventName: 'kibana:plugin_render_time', + key1: 'key1', + meta: { target: '/' }, + value1: 'value1', + }); + }); +}); diff --git a/packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.ts b/packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.ts index c1665e53f7bb6..2f0ed608ce1be 100644 --- a/packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.ts +++ b/packages/core/analytics/core-analytics-browser-internal/src/track_performance_measure_entries.ts @@ -8,6 +8,13 @@ import type { AnalyticsClient } from '@elastic/ebt/client'; import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; +const MAX_CUSTOM_METRICS = 9; +// The keys and values for the custom metrics are limited to 9 pairs +const ALLOWED_CUSTOM_METRICS_KEYS_VALUES = Array.from({ length: MAX_CUSTOM_METRICS }, (_, i) => [ + `key${i + 1}`, + `value${i + 1}`, +]).flat(); + export function trackPerformanceMeasureEntries(analytics: AnalyticsClient, isDevMode: boolean) { function perfObserver( list: PerformanceObserverEntryList, @@ -18,6 +25,19 @@ export function trackPerformanceMeasureEntries(analytics: AnalyticsClient, isDev if (entry.entryType === 'measure' && entry.detail?.type === 'kibana:performance') { const target = entry?.name; const duration = entry.duration; + const customMetrics = Object.keys(entry.detail?.customMetrics ?? {}).reduce( + (acc, metric) => { + if (ALLOWED_CUSTOM_METRICS_KEYS_VALUES.includes(metric)) { + return { + ...acc, + [metric]: entry.detail.customMetrics[metric], + }; + } + + return acc; + }, + {} + ); if (isDevMode) { if (!target) { @@ -47,6 +67,7 @@ export function trackPerformanceMeasureEntries(analytics: AnalyticsClient, isDev reportPerformanceMetricEvent(analytics, { eventName: entry.detail.eventName, duration, + ...customMetrics, meta: { target, }, diff --git a/packages/core/apps/core-apps-server-internal/src/core_app.test.ts b/packages/core/apps/core-apps-server-internal/src/core_app.test.ts index 3c5cfa068765c..b4b1837cad756 100644 --- a/packages/core/apps/core-apps-server-internal/src/core_app.test.ts +++ b/packages/core/apps/core-apps-server-internal/src/core_app.test.ts @@ -34,6 +34,7 @@ describe('CoreApp', () => { let httpResourcesRegistrar: ReturnType<typeof httpResourcesMock.createRegistrar>; beforeEach(() => { + jest.useFakeTimers(); coreContext = mockCoreContext.create(); internalCorePreboot = coreInternalLifecycleMock.createInternalPreboot(); @@ -55,37 +56,70 @@ describe('CoreApp', () => { afterEach(() => { registerBundleRoutesMock.mockReset(); + coreApp.stop(); + jest.clearAllTimers(); }); - describe('`/internal/core/_settings` route', () => { - it('is not registered by default', async () => { - const routerMock = mockRouter.create(); - internalCoreSetup.http.createRouter.mockReturnValue(routerMock); + describe('Dynamic Config feature', () => { + describe('`/internal/core/_settings` route', () => { + it('is not registered by default', async () => { + const routerMock = mockRouter.create(); + internalCoreSetup.http.createRouter.mockReturnValue(routerMock); + + const localCoreApp = new CoreAppsService(coreContext); + await localCoreApp.setup(internalCoreSetup, emptyPlugins()); + + expect(routerMock.versioned.put).not.toHaveBeenCalledWith( + expect.objectContaining({ + path: '/internal/core/_settings', + }) + ); + + // But the Saved Object is still registered + expect(internalCoreSetup.savedObjects.registerType).toHaveBeenCalledWith( + expect.objectContaining({ name: 'dynamic-config-overrides' }) + ); + }); + + it('is registered when enabled', async () => { + const routerMock = mockRouter.create(); + internalCoreSetup.http.createRouter.mockReturnValue(routerMock); - const localCoreApp = new CoreAppsService(coreContext); - await localCoreApp.setup(internalCoreSetup, emptyPlugins()); + coreContext.configService.atPath.mockReturnValue(of({ allowDynamicConfigOverrides: true })); + const localCoreApp = new CoreAppsService(coreContext); + await localCoreApp.setup(internalCoreSetup, emptyPlugins()); - expect(routerMock.versioned.put).not.toHaveBeenCalledWith( - expect.objectContaining({ + expect(routerMock.versioned.put).toHaveBeenCalledWith({ path: '/internal/core/_settings', - }) - ); - }); + access: 'internal', + options: { + tags: ['access:updateDynamicConfig'], + }, + }); + }); - it('is registered when enabled', async () => { - const routerMock = mockRouter.create(); - internalCoreSetup.http.createRouter.mockReturnValue(routerMock); + it('it fetches the persisted document when enabled', async () => { + const routerMock = mockRouter.create(); + internalCoreSetup.http.createRouter.mockReturnValue(routerMock); - coreContext.configService.atPath.mockReturnValue(of({ allowDynamicConfigOverrides: true })); - const localCoreApp = new CoreAppsService(coreContext); - await localCoreApp.setup(internalCoreSetup, emptyPlugins()); + coreContext.configService.atPath.mockReturnValue(of({ allowDynamicConfigOverrides: true })); + const localCoreApp = new CoreAppsService(coreContext); + await localCoreApp.setup(internalCoreSetup, emptyPlugins()); - expect(routerMock.versioned.put).toHaveBeenCalledWith({ - path: '/internal/core/_settings', - access: 'internal', - options: { - tags: ['access:updateDynamicConfig'], - }, + const internalCoreStart = coreInternalLifecycleMock.createInternalStart(); + localCoreApp.start(internalCoreStart); + + expect(internalCoreStart.savedObjects.createInternalRepository).toHaveBeenCalledWith([ + 'dynamic-config-overrides', + ]); + + const repository = + internalCoreStart.savedObjects.createInternalRepository.mock.results[0].value; + await jest.advanceTimersByTimeAsync(0); // "Advancing" 0ms is enough, but necessary to trigger the `timer` observable + expect(repository.get).toHaveBeenCalledWith( + 'dynamic-config-overrides', + 'dynamic-config-overrides' + ); }); }); }); diff --git a/packages/core/apps/core-apps-server-internal/src/core_app.ts b/packages/core/apps/core-apps-server-internal/src/core_app.ts index e6b7349d2edff..e9676c792292a 100644 --- a/packages/core/apps/core-apps-server-internal/src/core_app.ts +++ b/packages/core/apps/core-apps-server-internal/src/core_app.ts @@ -21,9 +21,27 @@ import type { } from '@kbn/core-http-server'; import type { UiPlugins } from '@kbn/core-plugins-base-server-internal'; import type { HttpResources, HttpResourcesServiceToolkit } from '@kbn/core-http-resources-server'; -import type { InternalCorePreboot, InternalCoreSetup } from '@kbn/core-lifecycle-server-internal'; +import type { + InternalCorePreboot, + InternalCoreSetup, + InternalCoreStart, +} from '@kbn/core-lifecycle-server-internal'; import type { InternalStaticAssets } from '@kbn/core-http-server-internal'; -import { firstValueFrom, map, type Observable } from 'rxjs'; +import { + combineLatest, + concatMap, + firstValueFrom, + map, + type Observable, + ReplaySubject, + shareReplay, + Subject, + takeUntil, + timer, +} from 'rxjs'; +import type { InternalSavedObjectsServiceStart } from '@kbn/core-saved-objects-server-internal'; +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { CoreAppConfig, type CoreAppConfigType, CoreAppPath } from './core_app_config'; import { registerBundleRoutes } from './bundle_routes'; import type { InternalCoreAppsServiceRequestHandlerContext } from './internal_types'; @@ -41,12 +59,17 @@ interface CommonRoutesParams { ) => Promise<IKibanaResponse>; } +const DYNAMIC_CONFIG_OVERRIDES_SO_TYPE = 'dynamic-config-overrides'; +const DYNAMIC_CONFIG_OVERRIDES_SO_ID = 'dynamic-config-overrides'; + /** @internal */ export class CoreAppsService { private readonly logger: Logger; private readonly env: Env; private readonly configService: IConfigService; private readonly config$: Observable<CoreAppConfig>; + private readonly savedObjectsStart$ = new ReplaySubject<InternalSavedObjectsServiceStart>(1); + private readonly stop$ = new Subject<void>(); constructor(core: CoreContext) { this.logger = core.logger.get('core-app'); @@ -70,8 +93,21 @@ export class CoreAppsService { async setup(coreSetup: InternalCoreSetup, uiPlugins: UiPlugins) { this.logger.debug('Setting up core app.'); const config = await firstValueFrom(this.config$); - this.registerDefaultRoutes(coreSetup, uiPlugins, config); + this.registerDefaultRoutes(coreSetup, uiPlugins); this.registerStaticDirs(coreSetup, uiPlugins); + this.maybeRegisterDynamicConfigurationFeature({ + config, + coreSetup, + savedObjectsStart$: this.savedObjectsStart$, + }); + } + + start(coreStart: InternalCoreStart) { + this.savedObjectsStart$.next(coreStart.savedObjects); + } + + stop() { + this.stop$.next(); } private registerPrebootDefaultRoutes(corePreboot: InternalCorePreboot, uiPlugins: UiPlugins) { @@ -100,11 +136,7 @@ export class CoreAppsService { }); } - private registerDefaultRoutes( - coreSetup: InternalCoreSetup, - uiPlugins: UiPlugins, - config: CoreAppConfig - ) { + private registerDefaultRoutes(coreSetup: InternalCoreSetup, uiPlugins: UiPlugins) { const httpSetup = coreSetup.http; const router = httpSetup.createRouter<InternalCoreAppsServiceRequestHandlerContext>(''); const resources = coreSetup.httpResources.createRegistrar(router); @@ -167,18 +199,80 @@ export class CoreAppsService { } } ); + } + + private maybeRegisterDynamicConfigurationFeature({ + config, + coreSetup, + savedObjectsStart$, + }: { + config: CoreAppConfig; + coreSetup: InternalCoreSetup; + savedObjectsStart$: Observable<InternalSavedObjectsServiceStart>; + }) { + // Always registering the Saved Objects to avoid ON/OFF conflicts in the migrations + coreSetup.savedObjects.registerType({ + name: DYNAMIC_CONFIG_OVERRIDES_SO_TYPE, + hidden: true, + hiddenFromHttpApis: true, + namespaceType: 'agnostic', + mappings: { + dynamic: false, + properties: {}, + }, + }); if (config.allowDynamicConfigOverrides) { - this.registerInternalCoreSettingsRoute(router); + const savedObjectsClient$ = savedObjectsStart$.pipe( + map((savedObjectsStart) => + savedObjectsStart.createInternalRepository([DYNAMIC_CONFIG_OVERRIDES_SO_TYPE]) + ), + shareReplay(1) + ); + + // Register the HTTP route + const router = coreSetup.http.createRouter<InternalCoreAppsServiceRequestHandlerContext>(''); + this.registerInternalCoreSettingsRoute(router, savedObjectsClient$); + + let latestOverrideVersion: string | undefined; // Use the document version to avoid calling override on every poll + // Poll for updates + combineLatest([savedObjectsClient$, timer(0, 10_000)]) + .pipe( + concatMap(async ([soClient]) => { + try { + const persistedOverrides = await soClient.get<Record<string, unknown>>( + DYNAMIC_CONFIG_OVERRIDES_SO_TYPE, + DYNAMIC_CONFIG_OVERRIDES_SO_ID + ); + if (latestOverrideVersion !== persistedOverrides.version) { + this.configService.setDynamicConfigOverrides(persistedOverrides.attributes); + latestOverrideVersion = persistedOverrides.version; + } + } catch (err) { + // Potential failures: + // - The SO document does not exist (404 error) => no need to log + // - The configuration overrides are invalid => they won't be applied and the validation error will be logged. + if (!SavedObjectsErrorHelpers.isNotFoundError(err)) { + this.logger.warn(`Failed to apply the persisted dynamic config overrides: ${err}`); + } + } + }), + takeUntil(this.stop$) + ) + .subscribe(); } } /** * Registers the HTTP API that allows updating in-memory the settings that opted-in to be dynamically updatable. * @param router {@link IRouter} + * @param savedObjectClient$ An observable of a {@link SavedObjectsClientContract | savedObjects client} that will be used to update the document * @private */ - private registerInternalCoreSettingsRoute(router: IRouter) { + private registerInternalCoreSettingsRoute( + router: IRouter, + savedObjectClient$: Observable<SavedObjectsClientContract> + ) { router.versioned .put({ path: '/internal/core/_settings', @@ -201,7 +295,12 @@ export class CoreAppsService { }, async (context, req, res) => { try { - this.configService.setDynamicConfigOverrides(req.body); + const newGlobalOverrides = this.configService.setDynamicConfigOverrides(req.body); + const soClient = await firstValueFrom(savedObjectClient$); + await soClient.create(DYNAMIC_CONFIG_OVERRIDES_SO_TYPE, newGlobalOverrides, { + id: DYNAMIC_CONFIG_OVERRIDES_SO_ID, + overwrite: true, + }); } catch (err) { if (err instanceof ValidationError) { return res.badRequest({ body: err }); diff --git a/packages/core/apps/core-apps-server-internal/tsconfig.json b/packages/core/apps/core-apps-server-internal/tsconfig.json index 35698e91f6ddb..cc84d62f4faa4 100644 --- a/packages/core/apps/core-apps-server-internal/tsconfig.json +++ b/packages/core/apps/core-apps-server-internal/tsconfig.json @@ -34,6 +34,9 @@ "@kbn/monaco", "@kbn/core-http-server-internal", "@kbn/core-http-router-server-internal", + "@kbn/core-saved-objects-server-internal", + "@kbn/core-saved-objects-api-server", + "@kbn/core-saved-objects-server", ], "exclude": [ "target/**/*", diff --git a/packages/core/http/core-http-server-internal/src/https_redirect_server.test.ts b/packages/core/http/core-http-server-internal/src/https_redirect_server.test.ts index 3123fe8bba06c..6a755ff2af890 100644 --- a/packages/core/http/core-http-server-internal/src/https_redirect_server.test.ts +++ b/packages/core/http/core-http-server-internal/src/https_redirect_server.test.ts @@ -6,6 +6,8 @@ * Side Public License, v 1. */ +import * as net from 'node:net'; + jest.mock('fs', () => ({ readFileSync: jest.fn(), })); @@ -28,14 +30,34 @@ function getServerListener(httpServer: HttpsRedirectServer) { return (httpServer as any).server.listener; } -beforeEach(() => { +async function getRandomAvailablePort(opts: Chance.Options): Promise<number> { + while (true) { + const candidatePort = chance.integer(opts); + try { + await new Promise<void>((resolve, reject) => { + const svr = net.createServer(); + svr.once('error', reject); + svr.listen({ host: '127.0.0.1', port: candidatePort }, () => { + svr.close(() => { + resolve(); + }); + }); + }); + return candidatePort; + } catch (err) { + // just keep trying to find another port + } + } +} + +beforeEach(async () => { config = { host: '127.0.0.1', maxPayload: new ByteSizeValue(1024), - port: chance.integer({ min: 10000, max: 15000 }), + port: await getRandomAvailablePort({ min: 10000, max: 15000 }), ssl: { enabled: true, - redirectHttpFromPort: chance.integer({ min: 20000, max: 30000 }), + redirectHttpFromPort: await getRandomAvailablePort({ min: 20000, max: 30000 }), }, cors: { enabled: false, @@ -55,7 +77,7 @@ test('throws if SSL is not enabled', async () => { ...config, ssl: { enabled: false, - redirectHttpFromPort: chance.integer({ min: 20000, max: 30000 }), + redirectHttpFromPort: await getRandomAvailablePort({ min: 20000, max: 30000 }), }, } as HttpConfig) ).rejects.toMatchSnapshot(); diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index d380bffd8b359..35acaf8324e1c 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -450,6 +450,8 @@ export class Server { userProfile: userProfileStart, }; + this.coreApp.start(this.coreStart); + await this.plugins.start(this.coreStart); await this.http.start(); @@ -469,6 +471,7 @@ export class Server { public async stop() { this.log.debug('stopping server'); + this.coreApp.stop(); await this.analytics.stop(); await this.http.stop(); // HTTP server has to stop before savedObjects and ES clients are closed to be able to gracefully attempt to resolve any pending requests await this.plugins.stop(); diff --git a/packages/kbn-alerts-grouping/src/components/alerts_grouping.test.tsx b/packages/kbn-alerts-grouping/src/components/alerts_grouping.test.tsx index c60ef03ca35e5..87517def778cd 100644 --- a/packages/kbn-alerts-grouping/src/components/alerts_grouping.test.tsx +++ b/packages/kbn-alerts-grouping/src/components/alerts_grouping.test.tsx @@ -33,8 +33,8 @@ jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_get_alerts_group_aggregati useGetAlertsGroupAggregationsQuery: jest.fn(), })); -jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alert_data_view', () => ({ - useAlertDataView: jest.fn().mockReturnValue({ dataViews: [{ fields: [] }] }), +jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view', () => ({ + useAlertsDataView: jest.fn().mockReturnValue({ dataView: { fields: [] } }), })); jest.mock('../contexts/alerts_grouping_context', () => { diff --git a/packages/kbn-alerts-grouping/src/components/alerts_grouping.tsx b/packages/kbn-alerts-grouping/src/components/alerts_grouping.tsx index 130997dd393ce..f17d794668371 100644 --- a/packages/kbn-alerts-grouping/src/components/alerts_grouping.tsx +++ b/packages/kbn-alerts-grouping/src/components/alerts_grouping.tsx @@ -20,7 +20,7 @@ import type { Filter } from '@kbn/es-query'; import { isNoneGroup, useGrouping } from '@kbn/grouping'; import { isEqual } from 'lodash/fp'; import { i18n } from '@kbn/i18n'; -import { useAlertDataView } from '@kbn/alerts-ui-shared'; +import { useAlertsDataView } from '@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { AlertsGroupingLevel, AlertsGroupingLevelProps } from './alerts_grouping_level'; import { AlertsGroupingProps } from '../types'; @@ -72,13 +72,12 @@ const AlertsGroupingInternal = (props: AlertsGroupingProps) => { const { dataViews, notifications, http } = services; const { grouping, updateGrouping } = useAlertsGroupingState(groupingId); - const { dataViews: alertDataViews } = useAlertDataView({ + const { dataView } = useAlertsDataView({ featureIds, dataViewsService: dataViews, http, toasts: notifications.toasts, }); - const dataView = useMemo(() => alertDataViews?.[0], [alertDataViews]); const [pageSize, setPageSize] = useLocalStorage<number[]>( `grouping-table-${groupingId}`, Array(MAX_GROUPING_LEVELS).fill(DEFAULT_PAGE_SIZE) diff --git a/packages/kbn-alerts-grouping/src/components/alerts_grouping_level.test.tsx b/packages/kbn-alerts-grouping/src/components/alerts_grouping_level.test.tsx index 45424f34d9cb4..f1295ee67dd73 100644 --- a/packages/kbn-alerts-grouping/src/components/alerts_grouping_level.test.tsx +++ b/packages/kbn-alerts-grouping/src/components/alerts_grouping_level.test.tsx @@ -24,7 +24,7 @@ mockUseGetAlertsGroupAggregationsQuery.mockReturnValue({ data: groupingSearchResponse, }); -jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alert_data_view', () => ({ +jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view', () => ({ useAlertDataView: jest.fn().mockReturnValue({ dataViews: [{ fields: [] }] }), })); diff --git a/packages/kbn-alerts-ui-shared/src/alert_filter_controls/alert_filter_controls.test.tsx b/packages/kbn-alerts-ui-shared/src/alert_filter_controls/alert_filter_controls.test.tsx new file mode 100644 index 0000000000000..54b74e4b2259a --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/alert_filter_controls/alert_filter_controls.test.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { AlertFilterControls, AlertFilterControlsProps } from './alert_filter_controls'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { DEFAULT_CONTROLS } from './constants'; +import { useAlertsDataView } from '../common/hooks/use_alerts_data_view'; +import { FilterGroup } from './filter_group'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; + +jest.mock('./filter_group'); +jest.mocked(FilterGroup).mockReturnValue(<span data-test-subj="filter-group" />); + +jest.mock('../common/hooks/use_alerts_data_view'); +jest.mocked(useAlertsDataView).mockReturnValue({ + isLoading: false, + dataView: { + title: '.alerts-*', + fields: [ + { + name: 'event.action', + type: 'string', + aggregatable: true, + searchable: true, + }, + ], + }, +}); + +const mockServices = { + http: httpServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + dataViews: dataViewPluginMocks.createStartContract(), + storage: class { + get = jest.fn(); + set = jest.fn(); + } as unknown as AlertFilterControlsProps['services']['storage'], +}; +mockServices.dataViews.clearInstanceCache = jest.fn().mockResolvedValue(undefined); + +const setFilters = jest.fn(); + +const ControlGroupRenderer = (() => ( + <span /> +)) as unknown as AlertFilterControlsProps['ControlGroupRenderer']; + +describe('AlertFilterControls', () => { + const props: AlertFilterControlsProps = { + featureIds: [AlertConsumers.STACK_ALERTS], + defaultControls: DEFAULT_CONTROLS, + dataViewSpec: { + id: 'alerts-filters-dv', + }, + onFiltersChange: setFilters, + services: mockServices, + chainingSystem: 'HIERARCHICAL', + ControlGroupRenderer, + }; + + it('renders the filter group', async () => { + render(<AlertFilterControls {...props} />); + + expect(await screen.findByTestId('filter-group')).toBeInTheDocument(); + }); + + it('creates a data view if a spec with an id is provided', () => { + render(<AlertFilterControls {...props} />); + + expect(mockServices.dataViews.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'alerts-filters-dv', + }) + ); + }); +}); diff --git a/packages/kbn-alerts-ui-shared/src/alert_filter_controls/alert_filter_controls.tsx b/packages/kbn-alerts-ui-shared/src/alert_filter_controls/alert_filter_controls.tsx index fc2b9fbbc207f..436480677d68b 100644 --- a/packages/kbn-alerts-ui-shared/src/alert_filter_controls/alert_filter_controls.tsx +++ b/packages/kbn-alerts-ui-shared/src/alert_filter_controls/alert_filter_controls.tsx @@ -15,7 +15,7 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; import { HttpStart } from '@kbn/core-http-browser'; import { NotificationsStart } from '@kbn/core-notifications-browser'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; -import { useAlertDataView } from '../..'; +import { useAlertsDataView } from '../..'; import { FilterGroupLoading } from './loading'; import { DEFAULT_CONTROLS } from './constants'; import { FilterGroup } from './filter_group'; @@ -94,7 +94,7 @@ export const AlertFilterControls = (props: AlertFilterControlsProps) => { ...restFilterItemGroupProps } = props; const [loadingPageFilters, setLoadingPageFilters] = useState(true); - const { dataViews: alertDataViews, loading: loadingDataViews } = useAlertDataView({ + const { dataView, isLoading: isLoadingDataView } = useAlertsDataView({ featureIds, dataViewsService: dataViews, http, @@ -102,14 +102,14 @@ export const AlertFilterControls = (props: AlertFilterControlsProps) => { }); useEffect(() => { - if (!loadingDataViews) { + if (!isLoadingDataView) { // If a data view spec is provided, create a new data view if (dataViewSpec?.id) { (async () => { // Creates an adhoc data view starting from the alert data view // and applying the overrides specified in the dataViewSpec const spec = { - ...(alertDataViews?.[0] ?? {}), + ...(dataView ?? {}), ...(dataViewSpec ?? {}), } as DataViewSpec; await dataViews.create(spec); @@ -121,7 +121,7 @@ export const AlertFilterControls = (props: AlertFilterControlsProps) => { } return () => dataViews.clearInstanceCache(); - }, [dataViewSpec, alertDataViews, dataViews, loadingDataViews]); + }, [dataView, dataViewSpec, dataViews, isLoadingDataView]); const handleFilterChanges = useCallback( (newFilters: Filter[]) => { diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx index 9a095200a8506..c187ace912e30 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx @@ -6,21 +6,23 @@ * Side Public License, v 1. */ -import { useCallback, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import type { Query, TimeRange } from '@kbn/es-query'; import type { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; -import { AlertConsumers } from '@kbn/rule-data-utils'; +import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; import { NO_INDEX_PATTERNS } from './constants'; import { SEARCH_BAR_PLACEHOLDER } from './translations'; import type { AlertsSearchBarProps, QueryLanguageType } from './types'; -import { useLoadRuleTypesQuery, useAlertDataView, useRuleAADFields } from '../common/hooks'; +import { useLoadRuleTypesQuery, useAlertsDataView, useRuleAADFields } from '../common/hooks'; const SA_ALERTS = { type: 'alerts', fields: {} } as SuggestionsAbstraction; +const EMPTY_FEATURE_IDS: ValidFeatureId[] = []; + export const AlertsSearchBar = ({ appName, disableQueryLanguageSwitcher = false, - featureIds, + featureIds = EMPTY_FEATURE_IDS, ruleTypeId, query, filters, @@ -40,8 +42,8 @@ export const AlertsSearchBar = ({ dataViewsService, }: AlertsSearchBarProps) => { const [queryLanguage, setQueryLanguage] = useState<QueryLanguageType>('kuery'); - const { dataViews, loading } = useAlertDataView({ - featureIds: featureIds ?? [], + const { dataView } = useAlertsDataView({ + featureIds, http, toasts, dataViewsService, @@ -52,8 +54,15 @@ export const AlertsSearchBar = ({ toasts, }); - const indexPatterns = - ruleTypeId && aadFields?.length ? [{ title: ruleTypeId, fields: aadFields }] : dataViews; + const indexPatterns = useMemo(() => { + if (ruleTypeId && aadFields?.length) { + return [{ title: ruleTypeId, fields: aadFields }]; + } + if (dataView) { + return [dataView]; + } + return null; + }, [aadFields, dataView, ruleTypeId]); const ruleType = useLoadRuleTypesQuery({ filteredRuleTypes: ruleTypeId !== undefined ? [ruleTypeId] : [], @@ -99,7 +108,7 @@ export const AlertsSearchBar = ({ appName, disableQueryLanguageSwitcher, // @ts-expect-error - DataView fields prop and SearchBar indexPatterns props are overly broad - indexPatterns: loading || fieldsLoading ? NO_INDEX_PATTERNS : indexPatterns, + indexPatterns: !indexPatterns || fieldsLoading ? NO_INDEX_PATTERNS : indexPatterns, placeholder, query: { query: query ?? '', language: queryLanguage }, filters, diff --git a/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_fields/fetch_alerts_fields.ts b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_fields/fetch_alerts_fields.ts index 3deafc37e8ce4..abe38a8fa3ab6 100644 --- a/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_fields/fetch_alerts_fields.ts +++ b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_fields/fetch_alerts_fields.ts @@ -11,10 +11,11 @@ import type { BrowserFields } from '@kbn/alerting-types'; import type { FetchAlertsFieldsParams } from './types'; import { BASE_RAC_ALERTS_API_PATH } from '../../constants'; -export const fetchAlertsFields = ({ http, featureIds }: FetchAlertsFieldsParams) => - http.get<{ browserFields: BrowserFields; fields: FieldDescriptor[] }>( +export const fetchAlertsFields = ({ http, featureIds }: FetchAlertsFieldsParams) => { + return http.get<{ browserFields: BrowserFields; fields: FieldDescriptor[] }>( `${BASE_RAC_ALERTS_API_PATH}/browser_fields`, { query: { featureIds }, } ); +}; diff --git a/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/fetch_alerts_index_names.test.ts b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/fetch_alerts_index_names.test.ts new file mode 100644 index 0000000000000..0eaa288cf999f --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/fetch_alerts_index_names.test.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { httpServiceMock } from '@kbn/core/public/mocks'; +import { fetchAlertsIndexNames } from '.'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { BASE_RAC_ALERTS_API_PATH } from '../../constants'; + +describe('fetchAlertsIndexNames', () => { + const http = httpServiceMock.createStartContract(); + + it('calls the alerts/index API with the correct parameters', async () => { + const featureIds = [AlertConsumers.STACK_ALERTS, AlertConsumers.APM]; + const indexNames = ['test-index']; + http.get.mockResolvedValueOnce({ + index_name: indexNames, + }); + const result = await fetchAlertsIndexNames({ http, featureIds }); + expect(result).toEqual(indexNames); + expect(http.get).toHaveBeenLastCalledWith(`${BASE_RAC_ALERTS_API_PATH}/index`, { + query: { features: featureIds.join(',') }, + }); + }); +}); diff --git a/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alert_index_names.ts b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/fetch_alerts_index_names.ts similarity index 52% rename from packages/kbn-alerts-ui-shared/src/common/apis/fetch_alert_index_names.ts rename to packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/fetch_alerts_index_names.ts index 9cf7fe1d3ebc8..29b8f97a5cdee 100644 --- a/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alert_index_names.ts +++ b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/fetch_alerts_index_names.ts @@ -6,21 +6,15 @@ * Side Public License, v 1. */ -import { HttpSetup } from '@kbn/core/public'; -import { BASE_RAC_ALERTS_API_PATH } from '../constants'; +import { BASE_RAC_ALERTS_API_PATH } from '../../constants'; +import { FetchAlertsIndexNamesParams } from './types'; -export async function fetchAlertIndexNames({ - http, - features, -}: { - http: HttpSetup; - features: string; -}): Promise<string[]> { - const { index_name: indexNamesStr = [] } = await http.get<{ index_name: string[] }>( +export const fetchAlertsIndexNames = async ({ http, featureIds }: FetchAlertsIndexNamesParams) => { + const { index_name: indexNames = [] } = await http.get<{ index_name: string[] }>( `${BASE_RAC_ALERTS_API_PATH}/index`, { - query: { features }, + query: { features: featureIds.join(',') }, } ); - return indexNamesStr; -} + return indexNames; +}; diff --git a/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/index.ts b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/index.ts new file mode 100644 index 0000000000000..c1f3202108554 --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/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 * from './fetch_alerts_index_names'; +export * from './types'; diff --git a/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/types.ts b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/types.ts new file mode 100644 index 0000000000000..ef74ad7dd09f6 --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/apis/fetch_alerts_index_names/types.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 { HttpSetup } from '@kbn/core-http-browser'; +import { ValidFeatureId } from '@kbn/rule-data-utils'; + +export interface FetchAlertsIndexNamesParams { + // Dependencies + http: HttpSetup; + + // Params + /** + * Array of feature ids used for authorization and area-based filtering + */ + featureIds: ValidFeatureId[]; +} diff --git a/packages/kbn-alerts-ui-shared/src/common/constants/alerts.ts b/packages/kbn-alerts-ui-shared/src/common/constants/alerts.ts new file mode 100644 index 0000000000000..858d5a3d53425 --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/constants/alerts.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { DataViewField } from '@kbn/data-views-plugin/common'; + +export const DEFAULT_ALERTS_PAGE_SIZE = 10; +export const EMPTY_AAD_FIELDS: DataViewField[] = []; diff --git a/packages/kbn-alerts-ui-shared/src/common/constants/index.ts b/packages/kbn-alerts-ui-shared/src/common/constants/index.ts index c3619df3ef99d..da5117831d092 100644 --- a/packages/kbn-alerts-ui-shared/src/common/constants/index.ts +++ b/packages/kbn-alerts-ui-shared/src/common/constants/index.ts @@ -6,5 +6,6 @@ * Side Public License, v 1. */ +export * from './alerts'; export * from './i18n_weekdays'; export * from './routes'; diff --git a/packages/kbn-alerts-ui-shared/src/common/constants/routes.ts b/packages/kbn-alerts-ui-shared/src/common/constants/routes.ts index 3bd42bbfd3f81..90ba57feb128b 100644 --- a/packages/kbn-alerts-ui-shared/src/common/constants/routes.ts +++ b/packages/kbn-alerts-ui-shared/src/common/constants/routes.ts @@ -6,14 +6,10 @@ * Side Public License, v 1. */ -import type { DataViewField } from '@kbn/data-views-plugin/common'; - export const ALERTS_FEATURE_ID = 'alerts'; export const BASE_ALERTING_API_PATH = '/api/alerting'; export const INTERNAL_BASE_ALERTING_API_PATH = '/internal/alerting'; export const BASE_RAC_ALERTS_API_PATH = '/internal/rac/alerts'; -export const EMPTY_AAD_FIELDS: DataViewField[] = []; export const BASE_TRIGGERS_ACTIONS_UI_API_PATH = '/internal/triggers_actions_ui'; -export const DEFAULT_ALERTS_PAGE_SIZE = 10; export const BASE_ACTION_API_PATH = '/api/actions'; export const INTERNAL_BASE_ACTION_API_PATH = '/internal/actions'; diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/index.ts b/packages/kbn-alerts-ui-shared/src/common/hooks/index.ts index ac7504eac2f82..06b1c77d5d3de 100644 --- a/packages/kbn-alerts-ui-shared/src/common/hooks/index.ts +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/index.ts @@ -6,16 +6,14 @@ * Side Public License, v 1. */ -export * from './use_alert_data_view'; -export * from './use_find_alerts_query'; +export * from './use_alerts_data_view'; +export * from './use_create_rule'; +export * from './use_get_alerts_group_aggregations_query'; +export * from './use_health_check'; +export * from './use_load_alerting_framework_health'; export * from './use_load_rule_types_query'; -export * from './use_rule_aad_fields'; export * from './use_load_ui_config'; -export * from './use_health_check'; export * from './use_load_ui_health'; -export * from './use_load_alerting_framework_health'; -export * from './use_create_rule'; -export * from './use_update_rule'; export * from './use_resolve_rule'; -export * from './use_search_alerts_query'; -export * from './use_get_alerts_group_aggregations_query'; +export * from './use_rule_aad_fields'; +export * from './use_update_rule'; diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_alert_data_view.ts b/packages/kbn-alerts-ui-shared/src/common/hooks/use_alert_data_view.ts deleted file mode 100644 index dfa0bd12f85ae..0000000000000 --- a/packages/kbn-alerts-ui-shared/src/common/hooks/use_alert_data_view.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { useEffect, useMemo, useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/common'; -import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; -import type { ToastsStart, HttpStart } from '@kbn/core/public'; - -import { useQuery } from '@tanstack/react-query'; -import { useFetchAlertsFieldsQuery } from './use_fetch_alerts_fields_query'; -import { fetchAlertIndexNames } from '../apis/fetch_alert_index_names'; - -export interface UseAlertDataViewResult { - dataViews?: DataView[]; - loading: boolean; -} - -export interface UseAlertDataViewProps { - featureIds: ValidFeatureId[]; - http: HttpStart; - dataViewsService: DataViewsContract; - toasts: ToastsStart; -} - -export function useAlertDataView(props: UseAlertDataViewProps): UseAlertDataViewResult { - const { http, dataViewsService, toasts, featureIds } = props; - - const [dataViews, setDataViews] = useState<DataView[]>([]); - const features = featureIds.sort().join(','); - const isOnlySecurity = featureIds.length === 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasSecurityAndO11yFeatureIds = - featureIds.length > 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasNoSecuritySolution = - featureIds.length > 0 && !isOnlySecurity && !hasSecurityAndO11yFeatureIds; - - const queryIndexNameFn = () => { - return fetchAlertIndexNames({ http, features }); - }; - - const onErrorFn = () => { - toasts.addDanger( - i18n.translate('alertsUIShared.hooks.useAlertDataView.useAlertDataMessage', { - defaultMessage: 'Unable to load alert data view', - }) - ); - }; - - const { - data: indexNames, - isSuccess: isIndexNameSuccess, - isInitialLoading: isIndexNameInitialLoading, - isLoading: isIndexNameLoading, - } = useQuery({ - queryKey: ['loadAlertIndexNames', features], - queryFn: queryIndexNameFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - staleTime: 60 * 1000, // To prevent duplicated requests - enabled: featureIds.length > 0 && !hasSecurityAndO11yFeatureIds, - }); - - const { - data: { fields: alertFields }, - isSuccess: isAlertFieldsSuccess, - isInitialLoading: isAlertFieldsInitialLoading, - isLoading: isAlertFieldsLoading, - } = useFetchAlertsFieldsQuery( - { http, featureIds }, - { - onError: onErrorFn, - refetchOnWindowFocus: false, - staleTime: 60 * 1000, - enabled: hasNoSecuritySolution, - } - ); - - useEffect(() => { - return () => { - dataViews.map((dv) => { - dataViewsService.clearInstanceCache(dv.id); - }); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dataViews]); - - // FUTURE ENGINEER this useEffect is for security solution user since - // we are using the user privilege to access the security alert index - useEffect(() => { - async function createDataView() { - const localDataview = await dataViewsService.create({ - title: (indexNames ?? []).join(','), - allowNoIndex: true, - }); - setDataViews([localDataview]); - } - - if (isOnlySecurity && isIndexNameSuccess) { - createDataView(); - } - }, [dataViewsService, indexNames, isIndexNameSuccess, isOnlySecurity]); - - // FUTURE ENGINEER this useEffect is for o11y and stack solution user since - // we are using the kibana user privilege to access the alert index - useEffect(() => { - if ( - indexNames && - alertFields && - !isOnlySecurity && - isAlertFieldsSuccess && - isIndexNameSuccess - ) { - setDataViews([ - { - title: (indexNames ?? []).join(','), - fieldFormatMap: {}, - fields: (alertFields ?? [])?.map((field) => { - return { - ...field, - ...(field.esTypes && field.esTypes.includes('flattened') ? { type: 'string' } : {}), - }; - }), - }, - ] as unknown as DataView[]); - } - }, [ - alertFields, - dataViewsService, - indexNames, - isIndexNameSuccess, - isOnlySecurity, - isAlertFieldsSuccess, - ]); - - return useMemo( - () => ({ - dataViews, - loading: - featureIds.length === 0 || hasSecurityAndO11yFeatureIds - ? false - : isOnlySecurity - ? isIndexNameInitialLoading || isIndexNameLoading || dataViews.length === 0 - : isIndexNameInitialLoading || - isIndexNameLoading || - isAlertFieldsInitialLoading || - isAlertFieldsLoading, - }), - [ - dataViews, - featureIds.length, - hasSecurityAndO11yFeatureIds, - isOnlySecurity, - isIndexNameInitialLoading, - isIndexNameLoading, - isAlertFieldsInitialLoading, - isAlertFieldsLoading, - ] - ); -} diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.test.tsx b/packages/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.test.tsx new file mode 100644 index 0000000000000..dbe46a9dbeefa --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.test.tsx @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, { FunctionComponent } from 'react'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks/dom'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; +import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { fetchAlertsIndexNames } from '../apis/fetch_alerts_index_names'; +import { fetchAlertsFields } from '../apis/fetch_alerts_fields'; +import { testQueryClientConfig } from '../test_utils/test_query_client_config'; +import { useAlertsDataView } from './use_alerts_data_view'; + +jest.mock('../apis/fetch_alerts_index_names'); +const mockFetchAlertsIndexNames = jest + .mocked(fetchAlertsIndexNames) + .mockResolvedValue([ + '.alerts-observability.uptime.alerts-*', + '.alerts-observability.metrics.alerts-*', + '.alerts-observability.logs.alerts-*', + '.alerts-observability.apm.alerts-*', + ]); + +jest.mock('../apis/fetch_alerts_fields'); +const mockFetchAlertsFields = jest + .mocked(fetchAlertsFields) + .mockResolvedValue({ browserFields: {}, fields: [] }); + +const mockDataView = { fields: [] } as unknown as DataView; + +const mockServices = { + http: httpServiceMock.createStartContract(), + toasts: notificationServiceMock.createStartContract().toasts, + dataViewsService: dataViewPluginMocks.createStartContract(), +}; +mockServices.dataViewsService.create.mockResolvedValue(mockDataView); + +const queryClient = new QueryClient(testQueryClientConfig); + +const wrapper: FunctionComponent = ({ children }) => ( + <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> +); + +describe('useAlertsDataView', () => { + const observabilityFeatureIds = [ + AlertConsumers.APM, + AlertConsumers.INFRASTRUCTURE, + AlertConsumers.LOGS, + AlertConsumers.UPTIME, + ]; + + beforeEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + }); + + it('starts with a loading state and without data', async () => { + const mockedAsyncDataView = { + isLoading: true, + dataView: undefined, + }; + + const { result, waitFor } = renderHook( + () => + useAlertsDataView({ + ...mockServices, + featureIds: observabilityFeatureIds, + }), + { + wrapper, + } + ); + + await waitFor(() => expect(result.current).toEqual(mockedAsyncDataView)); + }); + + it('fetches indexes and fields for non-siem feature ids, returning a DataViewBase object', async () => { + const { result, waitForValueToChange } = renderHook( + () => + useAlertsDataView({ + ...mockServices, + featureIds: observabilityFeatureIds, + }), + { + wrapper, + } + ); + + await waitForValueToChange(() => result.current.isLoading, { timeout: 5000 }); + + expect(mockFetchAlertsFields).toHaveBeenCalledTimes(1); + expect(mockFetchAlertsIndexNames).toHaveBeenCalledTimes(1); + expect(result.current.dataView).not.toBe(mockDataView); + }); + + it('only fetches index names for the siem feature id, returning a DataView', async () => { + const { result, waitFor } = renderHook( + () => useAlertsDataView({ ...mockServices, featureIds: [AlertConsumers.SIEM] }), + { + wrapper, + } + ); + + await waitFor(() => expect(mockFetchAlertsIndexNames).toHaveBeenCalledTimes(1)); + expect(mockFetchAlertsFields).toHaveBeenCalledTimes(0); + + await waitFor(() => expect(result.current.dataView).toBe(mockDataView)); + }); + + it('does not fetch anything if siem and other feature ids are mixed together', async () => { + const { result, waitFor } = renderHook( + () => + useAlertsDataView({ + ...mockServices, + featureIds: [AlertConsumers.SIEM, AlertConsumers.LOGS], + }), + { + wrapper, + } + ); + + await waitFor(() => + expect(result.current).toEqual({ + isLoading: false, + dataView: undefined, + }) + ); + expect(mockFetchAlertsIndexNames).toHaveBeenCalledTimes(0); + expect(mockFetchAlertsFields).toHaveBeenCalledTimes(0); + }); + + it('returns an undefined data view if any of the queries fails', async () => { + mockFetchAlertsIndexNames.mockRejectedValue('error'); + + const { result, waitFor } = renderHook( + () => useAlertsDataView({ ...mockServices, featureIds: observabilityFeatureIds }), + { + wrapper, + } + ); + + await waitFor(() => + expect(result.current).toEqual({ + isLoading: false, + dataView: undefined, + }) + ); + }); + + it('shows an error toast if any of the queries fails', async () => { + mockFetchAlertsIndexNames.mockRejectedValue('error'); + + const { waitFor } = renderHook( + () => useAlertsDataView({ ...mockServices, featureIds: observabilityFeatureIds }), + { + wrapper, + } + ); + + await waitFor(() => expect(mockServices.toasts.addDanger).toHaveBeenCalled()); + }); +}); diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.ts b/packages/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.ts new file mode 100644 index 0000000000000..5d8b3a9ee109f --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_alerts_data_view.ts @@ -0,0 +1,196 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useEffect, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { DataView, DataViewsContract, FieldSpec } from '@kbn/data-views-plugin/common'; +import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; +import type { ToastsStart, HttpStart } from '@kbn/core/public'; + +import { DataViewBase } from '@kbn/es-query'; +import { useVirtualDataViewQuery } from './use_virtual_data_view_query'; +import { useFetchAlertsFieldsQuery } from './use_fetch_alerts_fields_query'; +import { useFetchAlertsIndexNamesQuery } from './use_fetch_alerts_index_names_query'; + +export interface UseAlertsDataViewParams { + // Dependencies + http: HttpStart; + dataViewsService: DataViewsContract; + toasts: ToastsStart; + + // Params + /** + * Array of feature ids used for authorization and area-based filtering + * + * Security data views must be requested in isolation (i.e. `['siem']`). If mixed with + * other feature ids, the resulting data view will be empty. + */ + featureIds: ValidFeatureId[]; +} + +export interface UseAlertsDataViewResult { + isLoading: boolean; + dataView?: Omit<DataViewBase, 'fields'> & { fields: FieldSpec[] }; +} + +/** + * Resolves the DataView or DataViewBase object + * + * Returns undefined if any of the dependencies are in error state + */ +const resolveDataView = ({ + isError, + fields, + indexNames, + virtualDataView, +}: { + isError: boolean; + virtualDataView?: DataView; + indexNames?: string[]; + fields?: { fields: FieldSpec[] }; +}) => { + if (isError) { + return; + } + // When the only feature id is Security Solution, use an in-memory data view: + // their alerting authorization is based on end-user privileges, which allows us to create + // an actual data view + if (virtualDataView) { + return virtualDataView; + } + // For all other feature id combinations, compute the data view from the fetched index names and + // fields since the Kibana-user-based authorization wouldn't allow us to create a data view + if (indexNames) { + return { + title: indexNames.join(','), + fieldFormatMap: {}, + fields: (fields?.fields ?? []).map((field) => { + return { + ...field, + ...(field.esTypes && field.esTypes.includes('flattened') ? { type: 'string' } : {}), + }; + }), + }; + } +}; + +/** + * Computes a {@link DataViewBase} object for alerts indices based on the provided feature ids + * + * @returns + * A {@link DataViewBase} object, intentionally not typed as a complete {@link DataView} object + * since only Security Solution uses an actual in-memory data view (when `featureIds = ['siem']). + * In all other cases the data view is computed from the index names and fields fetched from the + * alerting APIs. + */ +export const useAlertsDataView = ({ + http, + dataViewsService, + toasts, + featureIds, +}: UseAlertsDataViewParams): UseAlertsDataViewResult => { + const includesSecurity = featureIds.includes(AlertConsumers.SIEM); + const isOnlySecurity = featureIds.length === 1 && includesSecurity; + const hasMixedFeatureIds = featureIds.length > 1 && includesSecurity; + + const { + data: indexNames, + isError: isIndexNamesError, + isLoading: isLoadingIndexNames, + isInitialLoading: isInitialLoadingIndexNames, + } = useFetchAlertsIndexNamesQuery( + { http, featureIds }, + { + // Don't fetch index names when featureIds includes both Security Solution and other features + enabled: !!featureIds.length && (isOnlySecurity || !includesSecurity), + } + ); + + const { + data: fields, + isError: isFieldsError, + isLoading: isLoadingFields, + isInitialLoading: isInitialLoadingFields, + } = useFetchAlertsFieldsQuery( + { http, featureIds }, + { + // Don't fetch fields when featureIds includes Security Solution + enabled: !!featureIds.length && !includesSecurity, + } + ); + + const { data: virtualDataView, isError: isVirtualDataViewError } = useVirtualDataViewQuery( + { + dataViewsService, + indexNames, + }, + { + // Create data view only when featureIds = ['siem'] and indexNames have been fetched + enabled: isOnlySecurity && !!indexNames?.length, + } + ); + + useEffect(() => { + if (isIndexNamesError || isFieldsError || isVirtualDataViewError) { + toasts.addDanger( + i18n.translate('alertsUIShared.hooks.useAlertDataView.fetchErrorMessage', { + defaultMessage: 'Unable to load alert data view', + }) + ); + } + }, [isFieldsError, isIndexNamesError, isVirtualDataViewError, toasts]); + + const dataView = useMemo( + () => + resolveDataView({ + isError: isIndexNamesError || isFieldsError || isVirtualDataViewError, + fields, + indexNames, + virtualDataView: !isOnlySecurity ? undefined : virtualDataView, + }), + [ + fields, + indexNames, + isFieldsError, + isIndexNamesError, + isOnlySecurity, + isVirtualDataViewError, + virtualDataView, + ] + ); + + return useMemo(() => { + let isLoading: boolean; + if (!featureIds.length || hasMixedFeatureIds) { + isLoading = false; + } else { + if (isOnlySecurity) { + isLoading = isInitialLoadingIndexNames || isLoadingIndexNames || !dataView; + } else { + isLoading = + isInitialLoadingIndexNames || + isLoadingIndexNames || + isInitialLoadingFields || + isLoadingFields; + } + } + return { + dataView, + isLoading, + }; + }, [ + dataView, + featureIds.length, + hasMixedFeatureIds, + isInitialLoadingFields, + isInitialLoadingIndexNames, + isLoadingFields, + isLoadingIndexNames, + isOnlySecurity, + ]); +}; diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.test.tsx b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.test.tsx index adcf3b37e38cb..20bdc513fa617 100644 --- a/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.test.tsx +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.test.tsx @@ -7,12 +7,14 @@ */ import React, { FunctionComponent } from 'react'; -import type { HttpSetup } from '@kbn/core-http-browser'; import { AlertConsumers } from '@kbn/rule-data-utils'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import * as ReactQuery from '@tanstack/react-query'; import { renderHook } from '@testing-library/react-hooks'; import { testQueryClientConfig } from '../test_utils/test_query_client_config'; import { useFetchAlertsFieldsQuery } from './use_fetch_alerts_fields_query'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; + +const { QueryClient, QueryClientProvider } = ReactQuery; const queryClient = new QueryClient(testQueryClientConfig); @@ -20,9 +22,9 @@ const wrapper: FunctionComponent = ({ children }) => ( <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> ); -const mockHttpClient = { - get: jest.fn(), -} as unknown as HttpSetup; +const useQuerySpy = jest.spyOn(ReactQuery, 'useQuery'); + +const mockHttpClient = httpServiceMock.createStartContract(); const emptyData = { browserFields: {}, fields: [] }; @@ -57,6 +59,30 @@ describe('useFetchAlertsFieldsQuery', () => { expect(result.current.data).toEqual(emptyData); }); + it('should correctly override the `enabled` option', () => { + const { rerender } = renderHook( + ({ featureIds, enabled }: { featureIds: AlertConsumers[]; enabled?: boolean }) => + useFetchAlertsFieldsQuery({ http: mockHttpClient, featureIds }, { enabled }), + { + wrapper, + initialProps: { + featureIds: ['apm'], + enabled: false, + }, + } + ); + + expect(useQuerySpy).toHaveBeenCalledWith(expect.objectContaining({ enabled: false })); + + rerender({ featureIds: [], enabled: true }); + + expect(useQuerySpy).toHaveBeenCalledWith(expect.objectContaining({ enabled: false })); + + rerender({ featureIds: ['apm'] }); + + expect(useQuerySpy).toHaveBeenCalledWith(expect.objectContaining({ enabled: true })); + }); + it('should call the api only once', async () => { const { result, rerender, waitForValueToChange } = renderHook( () => useFetchAlertsFieldsQuery({ http: mockHttpClient, featureIds: ['apm'] }), diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.ts b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.ts index 5d0f785335b22..f22ea573baf15 100644 --- a/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.ts +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query.ts @@ -27,7 +27,7 @@ export const useFetchAlertsFieldsQuery = ( { http, ...params }: UseFetchAlertsFieldsQueryParams, options?: Pick< QueryOptionsOverrides<typeof fetchAlertsFields>, - 'context' | 'onError' | 'refetchOnWindowFocus' | 'staleTime' | 'enabled' + 'placeholderData' | 'context' | 'onError' | 'refetchOnWindowFocus' | 'staleTime' | 'enabled' > ) => { const { featureIds } = params; @@ -37,10 +37,12 @@ export const useFetchAlertsFieldsQuery = ( ); return useQuery({ - queryKey: queryKeyPrefix.concat(JSON.stringify(featureIds)), + queryKey: queryKeyPrefix.concat(featureIds), queryFn: () => fetchAlertsFields({ http, featureIds: validFeatureIds }), - enabled: validFeatureIds.length > 0, - initialData: { browserFields: {}, fields: [] }, + placeholderData: { browserFields: {}, fields: [] }, + staleTime: 60 * 1000, + refetchOnWindowFocus: false, ...options, + enabled: validFeatureIds.length > 0 && (options?.enabled == null || options.enabled), }); }; diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_index_names_query.test.tsx b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_index_names_query.test.tsx new file mode 100644 index 0000000000000..ebd4d534e09ee --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_index_names_query.test.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 and the Server Side Public License, v 1; you may 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, { FunctionComponent } from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import { testQueryClientConfig } from '../test_utils/test_query_client_config'; +import { useFetchAlertsIndexNamesQuery } from './use_fetch_alerts_index_names_query'; +import { fetchAlertsIndexNames } from '../apis/fetch_alerts_index_names'; +import { httpServiceMock } from '@kbn/core-http-browser-mocks'; + +jest.mock('../apis/fetch_alerts_index_names'); + +const queryClient = new QueryClient(testQueryClientConfig); + +const wrapper: FunctionComponent = ({ children }) => ( + <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> +); + +const mockHttpClient = httpServiceMock.createStartContract(); +const mockFetchAlertsIndexNames = jest.mocked(fetchAlertsIndexNames); + +describe('useFetchAlertsIndexNamesQuery', () => { + beforeEach(() => { + mockFetchAlertsIndexNames.mockResolvedValue(['test-index']); + }); + + afterEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + }); + + it('does not fetch if featureIds is empty', () => { + renderHook(() => useFetchAlertsIndexNamesQuery({ http: mockHttpClient, featureIds: [] }), { + wrapper, + }); + + expect(mockFetchAlertsIndexNames).not.toHaveBeenCalled(); + }); + + it('calls fetchAlertsIndexNames with the correct parameters', () => { + renderHook(() => useFetchAlertsIndexNamesQuery({ http: mockHttpClient, featureIds: ['apm'] }), { + wrapper, + }); + + expect(mockFetchAlertsIndexNames).toHaveBeenCalledWith({ + http: mockHttpClient, + featureIds: ['apm'], + }); + }); + + it('correctly caches the index names', async () => { + const { result, rerender, waitForValueToChange } = renderHook( + () => useFetchAlertsIndexNamesQuery({ http: mockHttpClient, featureIds: ['apm'] }), + { + wrapper, + } + ); + + await waitForValueToChange(() => result.current.data); + + expect(mockFetchAlertsIndexNames).toHaveBeenCalledTimes(1); + + rerender(); + + expect(mockFetchAlertsIndexNames).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_index_names_query.ts b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_index_names_query.ts new file mode 100644 index 0000000000000..bc4c277ba5059 --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_fetch_alerts_index_names_query.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useQuery } from '@tanstack/react-query'; +import { + fetchAlertsIndexNames, + FetchAlertsIndexNamesParams, +} from '../apis/fetch_alerts_index_names'; +import type { QueryOptionsOverrides } from '../types/tanstack_query_utility_types'; + +export type UseFetchAlertsIndexNamesQueryParams = FetchAlertsIndexNamesParams; + +export const queryKeyPrefix = ['alerts', fetchAlertsIndexNames.name]; + +/** + * Fetch alerts index names feature ids + * + * When testing components that depend on this hook, prefer mocking the {@link fetchAlertsIndexNames} function instead of the hook itself. + * @external https://tanstack.com/query/v4/docs/framework/react/guides/testing + */ +export const useFetchAlertsIndexNamesQuery = ( + { http, featureIds }: UseFetchAlertsIndexNamesQueryParams, + options?: Pick< + QueryOptionsOverrides<typeof fetchAlertsIndexNames>, + 'context' | 'onError' | 'refetchOnWindowFocus' | 'staleTime' | 'enabled' + > +) => { + return useQuery({ + queryKey: queryKeyPrefix.concat(featureIds), + queryFn: () => fetchAlertsIndexNames({ http, featureIds }), + enabled: featureIds.length > 0, + staleTime: 60 * 1000, + refetchOnWindowFocus: false, + ...options, + }); +}; diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_search_alerts_query.test.tsx b/packages/kbn-alerts-ui-shared/src/common/hooks/use_search_alerts_query.test.tsx index 30624b22772cb..893e28c6dc4f9 100644 --- a/packages/kbn-alerts-ui-shared/src/common/hooks/use_search_alerts_query.test.tsx +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_search_alerts_query.test.tsx @@ -12,9 +12,10 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { IKibanaSearchResponse } from '@kbn/search-types'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { renderHook } from '@testing-library/react-hooks'; -import type { UseSearchAlertsQueryParams } from '../../..'; +import type { UseSearchAlertsQueryParams } from './use_search_alerts_query'; import { AlertsQueryContext } from '../contexts/alerts_query_context'; import { useSearchAlertsQuery } from './use_search_alerts_query'; +import { testQueryClientConfig } from '../test_utils/test_query_client_config'; const searchResponse = { id: '0', @@ -84,15 +85,7 @@ const expectedResponse: ReturnType<typeof useSearchAlertsQuery>['data'] = { ecsAlertsData: [], }; -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - cacheTime: 0, - staleTime: 0, - retry: false, - }, - }, -}); +const queryClient = new QueryClient(testQueryClientConfig); describe('useSearchAlertsQuery', () => { const mockDataPlugin = { diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_virtual_data_view_query.test.tsx b/packages/kbn-alerts-ui-shared/src/common/hooks/use_virtual_data_view_query.test.tsx new file mode 100644 index 0000000000000..4bf57fc918393 --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_virtual_data_view_query.test.tsx @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, { FunctionComponent } from 'react'; +import * as ReactQuery from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks'; +import { testQueryClientConfig } from '../test_utils/test_query_client_config'; +import { queryKeyPrefix, useVirtualDataViewQuery } from './use_virtual_data_view_query'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; + +const { QueryClient, QueryClientProvider } = ReactQuery; +const useQuerySpy = jest.spyOn(ReactQuery, 'useQuery'); + +const queryClient = new QueryClient(testQueryClientConfig); + +const wrapper: FunctionComponent = ({ children }) => ( + <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> +); + +const mockDataView = { fields: [] } as unknown as DataView; + +const mockDataViewsService = dataViewPluginMocks.createStartContract(); +mockDataViewsService.create.mockResolvedValue(mockDataView); +mockDataViewsService.clearInstanceCache = jest.fn(); + +describe('useVirtualDataViewQuery', () => { + afterEach(() => { + jest.clearAllMocks(); + queryClient.clear(); + }); + + it('does not create a data view if indexNames is empty or nullish', () => { + const { rerender } = renderHook( + ({ indexNames }: { indexNames: string[] }) => + useVirtualDataViewQuery({ dataViewsService: mockDataViewsService, indexNames }), + { + wrapper, + } + ); + + expect(mockDataViewsService.create).not.toHaveBeenCalled(); + rerender({ indexNames: [] }); + expect(useQuerySpy).toHaveBeenCalledWith( + expect.objectContaining({ enabled: false, queryKey: queryKeyPrefix.concat([]) }) + ); + + expect(mockDataViewsService.create).not.toHaveBeenCalled(); + }); + + it('calls dataViewsService.create with the correct index names', () => { + const indexNames = ['.alerts-stack*', '.alerts-o11y*']; + renderHook( + () => useVirtualDataViewQuery({ dataViewsService: mockDataViewsService, indexNames }), + { + wrapper, + } + ); + + expect(mockDataViewsService.create).toHaveBeenCalledWith({ + title: indexNames.join(','), + allowNoIndex: true, + }); + }); + + it('correctly caches the data view', () => { + const { rerender } = renderHook( + () => + useVirtualDataViewQuery({ + dataViewsService: mockDataViewsService, + indexNames: ['.alerts-*'], + }), + { + wrapper, + } + ); + + expect(mockDataViewsService.create).toHaveBeenCalledTimes(1); + + rerender(); + + expect(mockDataViewsService.create).toHaveBeenCalledTimes(1); + }); + + it('removes the data view from the instance cache on unmount', async () => { + const { result, waitForValueToChange, unmount } = renderHook( + () => + useVirtualDataViewQuery({ + dataViewsService: mockDataViewsService, + indexNames: ['.alerts-*'], + }), + { + wrapper, + } + ); + + await waitForValueToChange(() => result.current.data); + + unmount(); + + expect(mockDataViewsService.clearInstanceCache).toHaveBeenCalled(); + }); +}); diff --git a/packages/kbn-alerts-ui-shared/src/common/hooks/use_virtual_data_view_query.ts b/packages/kbn-alerts-ui-shared/src/common/hooks/use_virtual_data_view_query.ts new file mode 100644 index 0000000000000..46eeb8f3065ff --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/common/hooks/use_virtual_data_view_query.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { DataViewsContract } from '@kbn/data-views-plugin/common'; +import { useQuery } from '@tanstack/react-query'; +import { useEffect } from 'react'; +import { QueryOptionsOverrides } from '../types/tanstack_query_utility_types'; + +export interface UseVirtualDataViewParams { + // Dependencies + dataViewsService: DataViewsContract; + + // Params + /** + * The index names to create the data view for + */ + indexNames?: string[]; +} + +export const queryKeyPrefix = ['alerts', 'dataView']; + +/** + * Creates an in-memory data view, cached by index names + * + * When testing components that depend on this hook, prefer mocking {@link DataViewsContract}'s + * create and clearInstanceCache method instead of the hook itself. + * @external https://tanstack.com/query/v4/docs/framework/react/guides/testing + */ +export const useVirtualDataViewQuery = ( + { dataViewsService, indexNames }: UseVirtualDataViewParams, + options?: QueryOptionsOverrides<DataViewsContract['create']> +) => { + const query = useQuery({ + queryKey: queryKeyPrefix.concat(indexNames ?? []), + queryFn: () => + dataViewsService.create({ + title: (indexNames ?? []).join(','), + allowNoIndex: true, + }), + enabled: !!indexNames?.length, + staleTime: Infinity, + refetchOnWindowFocus: false, + ...options, + }); + + useEffect(() => { + // Cleanup the data view instance cache on unmount + if (query.data) { + return () => { + dataViewsService.clearInstanceCache(query.data.id); + }; + } + }, [dataViewsService, query.data]); + + return query; +}; diff --git a/packages/kbn-alerts-ui-shared/tsconfig.json b/packages/kbn-alerts-ui-shared/tsconfig.json index 653f26692bab1..79fc9d6fc9bdb 100644 --- a/packages/kbn-alerts-ui-shared/tsconfig.json +++ b/packages/kbn-alerts-ui-shared/tsconfig.json @@ -47,5 +47,7 @@ "@kbn/alerts-as-data-utils", "@kbn/test-jest-helpers", "@kbn/core-ui-settings-browser", + "@kbn/core-http-browser-mocks", + "@kbn/core-notifications-browser-mocks", ] } diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 4cec00e68dc42..d891c7ae5d095 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -284,6 +284,7 @@ "title", "version" ], + "dynamic-config-overrides": [], "endpoint:unified-user-artifact-manifest": [ "artifactIds", "policyId", diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 9242f775f7094..8bec33bcda085 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -975,6 +975,10 @@ } } }, + "dynamic-config-overrides": { + "dynamic": false, + "properties": {} + }, "endpoint:unified-user-artifact-manifest": { "dynamic": false, "properties": { diff --git a/packages/kbn-config/src/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts index 67a8aa0c3d75d..e6a5d3dc331a5 100644 --- a/packages/kbn-config/src/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -726,4 +726,19 @@ describe('Dynamic Overrides', () => { await firstValueFrom(configService.getConfig$().pipe(map((cfg) => cfg.toRaw()))) ).toStrictEqual({ namespace1: { key: 'another-value' } }); }); + + test('is able to remove a field when setting it to `null`', async () => { + configService.addDynamicConfigPaths('namespace1', ['key']); + configService.setDynamicConfigOverrides({ 'namespace1.key': 'another-value' }); + + expect( + await firstValueFrom(configService.getConfig$().pipe(map((cfg) => cfg.toRaw()))) + ).toStrictEqual({ namespace1: { key: 'another-value' } }); + + configService.setDynamicConfigOverrides({ 'namespace1.key': null }); + + expect( + await firstValueFrom(configService.getConfig$().pipe(map((cfg) => cfg.toRaw()))) + ).toStrictEqual({ namespace1: {} }); + }); }); diff --git a/packages/kbn-config/src/config_service.ts b/packages/kbn-config/src/config_service.ts index 51a83f82664e2..7aba4861c7423 100644 --- a/packages/kbn-config/src/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -8,7 +8,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { SchemaTypeError, Type, ValidationError } from '@kbn/config-schema'; -import { cloneDeep, isEqual, merge } from 'lodash'; +import { cloneDeep, isEqual, merge, unset } from 'lodash'; import { set } from '@kbn/safer-lodash-set'; import { BehaviorSubject, combineLatest, firstValueFrom, Observable, identity } from 'rxjs'; import { distinctUntilChanged, first, map, shareReplay, tap } from 'rxjs'; @@ -64,7 +64,10 @@ export class ConfigService { private readonly schemas = new Map<string, Type<unknown>>(); private readonly deprecations = new BehaviorSubject<ConfigDeprecationWithContext[]>([]); private readonly dynamicPaths = new Map<string, string[]>(); - private readonly overrides$ = new BehaviorSubject<Record<string, unknown>>({}); + private readonly overrides$ = new BehaviorSubject<{ + additions: Record<string, unknown>; + removals: string[]; + }>({ additions: {}, removals: [] }); private readonly handledDeprecatedConfigs = new Map<string, DeprecatedConfigDetails[]>(); constructor( @@ -85,7 +88,8 @@ export class ConfigService { this.overrides$, ]).pipe( map(([rawConfig, deprecations, overrides]) => { - const overridden = merge(rawConfig, overrides); + const overridden = merge(rawConfig, overrides.additions); + overrides.removals.forEach((key) => unset(overridden, key)); const migrated = applyDeprecations(overridden, deprecations); this.deprecatedConfigPaths.next(migrated.changedPaths); return new ObjectToConfigAdapter(migrated.config); @@ -254,15 +258,23 @@ export class ConfigService { * @param newOverrides */ public setDynamicConfigOverrides(newOverrides: Record<string, unknown>) { - const globalOverrides = cloneDeep(this.overrides$.value); + const globalOverrides = cloneDeep(this.overrides$.value.additions); const flattenedOverrides = getFlattenedObject(newOverrides); const validateWithNamespace = new Set<string>(); + const flattenedKeysToRemove: string[] = []; // We don't want to remove keys until all the validations have been applied. + keyLoop: for (const key in flattenedOverrides) { // this if is enforced by an eslint rule :shrug: if (key in flattenedOverrides) { + // If set to `null`, delete the config from the overrides. + if (flattenedOverrides[key] === null) { + flattenedKeysToRemove.push(key); + continue; + } + for (const [configPath, dynamicConfigKeys] of this.dynamicPaths.entries()) { if ( key.startsWith(`${configPath}.`) && @@ -282,13 +294,17 @@ export class ConfigService { } } - const globalOverridesAsConfig = new ObjectToConfigAdapter( - merge({}, this.lastConfig, globalOverrides) - ); + const rawConfig = merge({}, this.lastConfig, globalOverrides); + flattenedKeysToRemove.forEach((key) => { + unset(globalOverrides, key); + unset(rawConfig, key); + }); + const globalOverridesAsConfig = new ObjectToConfigAdapter(rawConfig); validateWithNamespace.forEach((ns) => this.validateAtPath(ns, globalOverridesAsConfig.get(ns))); - this.overrides$.next(globalOverrides); + this.overrides$.next({ additions: globalOverrides, removals: flattenedKeysToRemove }); + return globalOverrides; } private async logDeprecation() { diff --git a/packages/kbn-ebt-tools/src/performance_metrics/context/performance_context.tsx b/packages/kbn-ebt-tools/src/performance_metrics/context/performance_context.tsx index 72f3d39d3c825..a59a458656825 100644 --- a/packages/kbn-ebt-tools/src/performance_metrics/context/performance_context.tsx +++ b/packages/kbn-ebt-tools/src/performance_metrics/context/performance_context.tsx @@ -11,6 +11,9 @@ import { afterFrame } from '@elastic/apm-rum-core'; import { useLocation } from 'react-router-dom'; import { perfomanceMarkers } from '../performance_markers'; import { PerformanceApi, PerformanceContext } from './use_performance_context'; +import { PerformanceMetricEvent } from '../../performance_metric_events'; + +export type CustomMetrics = Omit<PerformanceMetricEvent, 'eventName' | 'meta' | 'duration'>; function measureInteraction() { performance.mark(perfomanceMarkers.startPageChange); @@ -19,13 +22,18 @@ function measureInteraction() { /** * Marks the end of the page ready state and measures the performance between the start of the page change and the end of the page ready state. * @param pathname - The pathname of the page. + * @param customMetrics - Custom metrics to be included in the performance measure. */ - pageReady(pathname: string) { + pageReady(pathname: string, customMetrics?: CustomMetrics) { performance.mark(perfomanceMarkers.endPageReady); if (!trackedRoutes.includes(pathname)) { performance.measure(pathname, { - detail: { eventName: 'kibana:plugin_render_time', type: 'kibana:performance' }, + detail: { + eventName: 'kibana:plugin_render_time', + type: 'kibana:performance', + customMetrics, + }, start: perfomanceMarkers.startPageChange, end: perfomanceMarkers.endPageReady, }); @@ -52,9 +60,9 @@ export function PerformanceContextProvider({ children }: { children: React.React const api = useMemo<PerformanceApi>( () => ({ - onPageReady() { + onPageReady(customMetrics) { if (isRendered) { - interaction.pageReady(location.pathname); + interaction.pageReady(location.pathname, customMetrics); } }, }), diff --git a/packages/kbn-ebt-tools/src/performance_metrics/context/use_performance_context.tsx b/packages/kbn-ebt-tools/src/performance_metrics/context/use_performance_context.tsx index 3bc083186a869..28995b3bef336 100644 --- a/packages/kbn-ebt-tools/src/performance_metrics/context/use_performance_context.tsx +++ b/packages/kbn-ebt-tools/src/performance_metrics/context/use_performance_context.tsx @@ -7,9 +7,14 @@ */ import { createContext, useContext } from 'react'; +import { CustomMetrics } from './performance_context'; export interface PerformanceApi { - onPageReady(): void; + /** + * Marks the end of the page ready state and measures the performance between the start of the page change and the end of the page ready state. + * @param customMetrics - Custom metrics to be included in the performance measure. + */ + onPageReady(customMetrics?: CustomMetrics): void; } export const PerformanceContext = createContext<PerformanceApi | undefined>(undefined); diff --git a/packages/kbn-es/index.ts b/packages/kbn-es/index.ts index 2f241828f400d..12bb617f3ecfb 100644 --- a/packages/kbn-es/index.ts +++ b/packages/kbn-es/index.ts @@ -21,4 +21,4 @@ export { readRolesDescriptorsFromResource, } from './src/utils'; export type { ArtifactLicense } from './src/artifact'; -export { SERVERLESS_ROLES_ROOT_PATH } from './src/paths'; +export { SERVERLESS_ROLES_ROOT_PATH, STATEFUL_ROLES_ROOT_PATH } from './src/paths'; diff --git a/packages/kbn-es/src/paths.ts b/packages/kbn-es/src/paths.ts index 2ee3ea7c7c205..b407630333c7e 100644 --- a/packages/kbn-es/src/paths.ts +++ b/packages/kbn-es/src/paths.ts @@ -25,6 +25,8 @@ export const ES_CONFIG = 'config/elasticsearch.yml'; export const ES_KEYSTORE_BIN = maybeUseBat('./bin/elasticsearch-keystore'); +export const STATEFUL_ROLES_ROOT_PATH = resolve(__dirname, './stateful_resources'); + export const SERVERLESS_OPERATOR_USERS_PATH = resolve( __dirname, './serverless_resources/operator_users.yml' diff --git a/packages/kbn-es/src/stateful_resources/roles.yml b/packages/kbn-es/src/stateful_resources/roles.yml new file mode 100644 index 0000000000000..49ae1fafad958 --- /dev/null +++ b/packages/kbn-es/src/stateful_resources/roles.yml @@ -0,0 +1,130 @@ +# ----- +# This file is for information purpose only. 'viewer' and 'editor' roles are defined in stateful Elasticsearch by default +# Source: https://github.com/elastic/elasticsearch/blob/4272164530807787d4d8b991e3095a6e79176dbf/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java#L861-L952 +# Note: inconsistency between these roles definition and the same roles of serverless project may break FTR deployment-agnostic tests +# ----- +viewer: + cluster: [] + indices: + - names: + - '.alerts*' + - '.preview.alerts*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - '.items-*' + - '.lists-*' + - '.siem-signals*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - '/~(([.]|ilm-history-).*)/' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - '.profiling-*' + - 'profiling-*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + applications: + - application: 'kibana-.kibana' + privileges: + - 'read' + resources: + - '*' + run_as: [] + +editor: + cluster: [] + indices: + - names: + - 'observability-annotations' + privileges: + - 'read' + - 'view_index_metadata' + - 'write' + allow_restricted_indices: false + - names: + - '.items-*' + - '.lists-*' + - '.siem-signals*' + privileges: + - 'maintenance' + - 'read' + - 'view_index_metadata' + - 'write' + allow_restricted_indices: false + - names: + - '/~(([.]|ilm-history-).*)/' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - '.profiling-*' + - 'profiling-*' + privileges: + - 'read' + - 'view_index_metadata' + allow_restricted_indices: false + - names: + - '.alerts*' + - '.internal.alerts*' + - '.internal.preview.alerts*' + - '.preview.alerts*' + privileges: + - 'maintenance' + - 'read' + - 'view_index_metadata' + - 'write' + allow_restricted_indices: false + applications: + - application: 'kibana-.kibana' + privileges: + - 'all' + resources: + - '*' + run_as: [] + +# Admin role without 'remote_indices' access definition +# There is no such built-in role in stateful, and it's a role "similar" to the built-in 'admin' role in serverless +admin: + # TODO: 'all' should be replaced with explicit list both here and serverless for deployment-agnostic tests with 'admin' role to be compatible + cluster: ['all'] + indices: + - names: ['*'] + privileges: ['all'] + allow_restricted_indices: false + - names: ['*'] + privileges: + - 'monitor' + - 'read' + - 'read_cross_cluster' + - 'view_index_metadata' + allow_restricted_indices: true + applications: + - application: '*' + privileges: ['*'] + resources: ['*'] + run_as: ['*'] + +# temporarily added for testing purpose +system_indices_superuser: + cluster: ['all'] + indices: + - names: ['*'] + privileges: ['all'] + allow_restricted_indices: true + applications: + - application: '*' + privileges: ['*'] + resources: ['*'] + run_as: ['*'] diff --git a/packages/kbn-es/src/utils/docker.ts b/packages/kbn-es/src/utils/docker.ts index a0b11818ccd73..39e947793ae09 100644 --- a/packages/kbn-es/src/utils/docker.ts +++ b/packages/kbn-es/src/utils/docker.ts @@ -382,6 +382,12 @@ export async function maybeCreateDockerNetwork(log: ToolingLog) { log.indent(-4); } +const RETRYABLE_DOCKER_PULL_ERROR_MESSAGES = [ + 'connection refused', + 'i/o timeout', + 'Client.Timeout', +]; + /** * * Pull a Docker image if needed. Ensures latest image. @@ -407,8 +413,12 @@ ${message}`; { retries: 2, onFailedAttempt: (error) => { - // Only retry if `connection refused` is found in the error message. - if (!error?.message?.includes('connection refused')) { + // Only retry if retryable error messages are found in the error message. + if ( + RETRYABLE_DOCKER_PULL_ERROR_MESSAGES.every( + (msg) => !error?.message?.includes('connection refused') + ) + ) { throw error; } }, diff --git a/packages/kbn-esql-ast/src/ast_factory.ts b/packages/kbn-esql-ast/src/ast_factory.ts index cd73bc79a3887..09d8cc174b842 100644 --- a/packages/kbn-esql-ast/src/ast_factory.ts +++ b/packages/kbn-esql-ast/src/ast_factory.ts @@ -30,6 +30,7 @@ import { type MetaCommandContext, type MetricsCommandContext, IndexPatternContext, + InlinestatsCommandContext, } from './antlr/esql_parser'; import { default as ESQLParserListener } from './antlr/esql_parser_listener'; import { @@ -197,6 +198,23 @@ export class AstListener implements ESQLParserListener { } } + /** + * Exit a parse tree produced by `esql_parser.inlinestatsCommand`. + * @param ctx the parse tree + */ + exitInlinestatsCommand(ctx: InlinestatsCommandContext) { + const command = createCommand('inlinestats', ctx); + this.ast.push(command); + + // STATS expression is optional + if (ctx._stats) { + command.args.push(...collectAllFields(ctx.fields(0))); + } + if (ctx._grouping) { + command.args.push(...visitByOption(ctx, ctx._stats ? ctx.fields(1) : ctx.fields(0))); + } + } + /** * Exit a parse tree produced by `esql_parser.limitCommand`. * @param ctx the parse tree diff --git a/packages/kbn-esql-ast/src/ast_walker.ts b/packages/kbn-esql-ast/src/ast_walker.ts index 3a0f182215c3e..7400d23d0dba2 100644 --- a/packages/kbn-esql-ast/src/ast_walker.ts +++ b/packages/kbn-esql-ast/src/ast_walker.ts @@ -61,6 +61,7 @@ import { InputNamedOrPositionalParamContext, InputParamContext, IndexPatternContext, + InlinestatsCommandContext, } from './antlr/esql_parser'; import { createSource, @@ -594,7 +595,10 @@ export function collectAllFields(ctx: FieldsContext | undefined): ESQLAstField[] return ast; } -export function visitByOption(ctx: StatsCommandContext, expr: FieldsContext | undefined) { +export function visitByOption( + ctx: StatsCommandContext | InlinestatsCommandContext, + expr: FieldsContext | undefined +) { if (!ctx.BY() || !expr) { return []; } diff --git a/packages/kbn-esql-ast/src/pretty_print/pretty_print_one_line.test.ts b/packages/kbn-esql-ast/src/pretty_print/pretty_print_one_line.test.ts new file mode 100644 index 0000000000000..2112eeabe483b --- /dev/null +++ b/packages/kbn-esql-ast/src/pretty_print/pretty_print_one_line.test.ts @@ -0,0 +1,352 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { getAstAndSyntaxErrors } from '../ast_parser'; +import { prettyPrintOneLine } from './pretty_print_one_line'; + +const reprint = (src: string) => { + const { ast } = getAstAndSyntaxErrors(src); + const text = prettyPrintOneLine(ast); + + // console.log(JSON.stringify(ast, null, 2)); + + return { text }; +}; + +describe('commands', () => { + describe('FROM', () => { + test('FROM command with a single source', () => { + const { text } = reprint('FROM index1'); + + expect(text).toBe('FROM index1'); + }); + + test('FROM command with multiple indices', () => { + const { text } = reprint('from index1, index2, index3'); + + expect(text).toBe('FROM index1, index2, index3'); + }); + + test('FROM command with METADATA', () => { + const { text } = reprint('FROM index1, index2 METADATA field1, field2'); + + expect(text).toBe('FROM index1, index2 METADATA field1, field2'); + }); + }); + + describe('SORT', () => { + test('order expression with no modifier', () => { + const { text } = reprint('FROM a | SORT b'); + + expect(text).toBe('FROM a | SORT b'); + }); + + /** @todo Enable once order expressions are supported. */ + test.skip('order expression with ASC modifier', () => { + const { text } = reprint('FROM a | SORT b ASC'); + + expect(text).toBe('FROM a | SORT b ASC'); + }); + + /** @todo Enable once order expressions are supported. */ + test.skip('order expression with ASC and NULLS FIRST modifier', () => { + const { text } = reprint('FROM a | SORT b ASC NULLS FIRST'); + + expect(text).toBe('FROM a | SORT b ASC NULLS FIRST'); + }); + }); + + describe('EXPLAIN', () => { + /** @todo Enable once query expressions are supported. */ + test.skip('a nested query', () => { + const { text } = reprint('EXPLAIN [ FROM 1 ]'); + + expect(text).toBe('EXPLAIN [ FROM 1 ]'); + }); + }); + + describe('SHOW', () => { + /** @todo Enable once show command args are parsed as columns. */ + test.skip('info page', () => { + const { text } = reprint('SHOW info'); + + expect(text).toBe('SHOW info'); + }); + }); + + describe('META', () => { + /** @todo Enable once show command args are parsed as columns. */ + test.skip('functions page', () => { + const { text } = reprint('META functions'); + + expect(text).toBe('META functions'); + }); + }); + + describe('STATS', () => { + test('with aggregates assignment', () => { + const { text } = reprint('FROM a | STATS var = agg(123, fn(true))'); + + expect(text).toBe('FROM a | STATS var = AGG(123, FN(TRUE))'); + }); + + test('with BY clause', () => { + const { text } = reprint('FROM a | STATS a(1), b(2) by asdf'); + + expect(text).toBe('FROM a | STATS A(1), B(2) BY asdf'); + }); + }); +}); + +describe('expressions', () => { + describe('source expressions', () => { + test('simple source expression', () => { + const { text } = reprint('from source'); + + expect(text).toBe('FROM source'); + }); + + test('sources with dots', () => { + const { text } = reprint('FROM a.b.c'); + + expect(text).toBe('FROM a.b.c'); + }); + + test('sources with slashes', () => { + const { text } = reprint('FROM a/b/c'); + + expect(text).toBe('FROM a/b/c'); + }); + + test('cluster source', () => { + const { text } = reprint('FROM cluster:index'); + + expect(text).toBe('FROM cluster:index'); + }); + + test('quoted source', () => { + const { text } = reprint('FROM "quoted"'); + + expect(text).toBe('FROM quoted'); + }); + + test('triple-quoted source', () => { + const { text } = reprint('FROM """quoted"""'); + + expect(text).toBe('FROM quoted'); + }); + }); + + describe('column expressions', () => { + test('simple columns expressions', () => { + const { text } = reprint('FROM a METADATA column1, _column2'); + + expect(text).toBe('FROM a METADATA column1, _column2'); + }); + + test('nested fields', () => { + const { text } = reprint('FROM a | KEEP a.b'); + + expect(text).toBe('FROM a | KEEP a.b'); + }); + + // Un-skip when "IdentifierPattern" is parsed correctly. + test.skip('quoted nested fields', () => { + const { text } = reprint('FROM index | KEEP `a`.`b`, c.`d`'); + + expect(text).toBe('FROM index | KEEP a.b, c.d'); + }); + + // Un-skip when identifier names are escaped correctly. + test.skip('special character in identifier', () => { + const { text } = reprint('FROM a | KEEP `a 👉 b`, a.`✅`'); + + expect(text).toBe('FROM a | KEEP `a 👉 b`, a.`✅`'); + }); + }); + + describe('"function" expressions', () => { + describe('function call expression', () => { + test('no argument function', () => { + const { text } = reprint('ROW fn()'); + + expect(text).toBe('ROW FN()'); + }); + + test('functions with arguments', () => { + const { text } = reprint('ROW gg(1), wp(1, 2, 3)'); + + expect(text).toBe('ROW GG(1), WP(1, 2, 3)'); + }); + + test('functions with star argument', () => { + const { text } = reprint('ROW f(*)'); + + expect(text).toBe('ROW F(*)'); + }); + }); + + describe('unary expression', () => { + test('NOT expression', () => { + const { text } = reprint('ROW NOT a'); + + expect(text).toBe('ROW NOT a'); + }); + }); + + describe('postfix unary expression', () => { + test('IS NOT NULL expression', () => { + const { text } = reprint('ROW a IS NOT NULL'); + + expect(text).toBe('ROW a IS NOT NULL'); + }); + }); + + describe('binary expression expression', () => { + test('arithmetic expression', () => { + const { text } = reprint('ROW 1 + 2'); + + expect(text).toBe('ROW 1 + 2'); + }); + + test('assignment expression', () => { + const { text } = reprint('FROM a | STATS a != 1'); + + expect(text).toBe('FROM a | STATS a != 1'); + }); + + test('regex expression - 1', () => { + const { text } = reprint('FROM a | WHERE a NOT RLIKE "a"'); + + expect(text).toBe('FROM a | WHERE a NOT RLIKE "a"'); + }); + + test('regex expression - 2', () => { + const { text } = reprint('FROM a | WHERE a LIKE "b"'); + + expect(text).toBe('FROM a | WHERE a LIKE "b"'); + }); + }); + }); + + describe('literals expressions', () => { + describe('numeric literal', () => { + test('null', () => { + const { text } = reprint('ROW null'); + + expect(text).toBe('ROW NULL'); + }); + + test('boolean', () => { + expect(reprint('ROW true').text).toBe('ROW TRUE'); + expect(reprint('ROW false').text).toBe('ROW FALSE'); + }); + + test('integer', () => { + const { text } = reprint('ROW 1'); + + expect(text).toBe('ROW 1'); + }); + + test('decimal', () => { + const { text } = reprint('ROW 1.2'); + + expect(text).toBe('ROW 1.2'); + }); + + test('string', () => { + const { text } = reprint('ROW "abc"'); + + expect(text).toBe('ROW "abc"'); + }); + + test('string w/ special chars', () => { + const { text } = reprint('ROW "as \\" 👍"'); + + expect(text).toBe('ROW "as \\" 👍"'); + }); + + describe('params', () => { + test('unnamed', () => { + const { text } = reprint('ROW ?'); + + expect(text).toBe('ROW ?'); + }); + + test('named', () => { + const { text } = reprint('ROW ?kappa'); + + expect(text).toBe('ROW ?kappa'); + }); + + test('positional', () => { + const { text } = reprint('ROW ?42'); + + expect(text).toBe('ROW ?42'); + }); + }); + }); + }); + + describe('list literal expressions', () => { + describe('integer list', () => { + test('one element list', () => { + expect(reprint('ROW [1]').text).toBe('ROW [1]'); + }); + + test('multiple elements', () => { + expect(reprint('ROW [1, 2]').text).toBe('ROW [1, 2]'); + expect(reprint('ROW [1, 2, -1]').text).toBe('ROW [1, 2, -1]'); + }); + }); + + describe('boolean list', () => { + test('one element list', () => { + expect(reprint('ROW [true]').text).toBe('ROW [TRUE]'); + }); + + test('multiple elements', () => { + expect(reprint('ROW [TRUE, false]').text).toBe('ROW [TRUE, FALSE]'); + expect(reprint('ROW [false, FALSE, false]').text).toBe('ROW [FALSE, FALSE, FALSE]'); + }); + }); + + describe('string list', () => { + test('one element list', () => { + expect(reprint('ROW ["a"]').text).toBe('ROW ["a"]'); + }); + + test('multiple elements', () => { + expect(reprint('ROW ["a", "b"]').text).toBe('ROW ["a", "b"]'); + expect(reprint('ROW ["foo", "42", "boden"]').text).toBe('ROW ["foo", "42", "boden"]'); + }); + }); + }); + + describe('cast expressions', () => { + test('various', () => { + expect(reprint('ROW a::string').text).toBe('ROW a::string'); + expect(reprint('ROW 123::string').text).toBe('ROW 123::string'); + expect(reprint('ROW "asdf"::number').text).toBe('ROW "asdf"::number'); + }); + }); + + describe('time interval expression', () => { + test('days', () => { + const { text } = reprint('ROW 1 d'); + + expect(text).toBe('ROW 1d'); + }); + + test('years', () => { + const { text } = reprint('ROW 42y'); + + expect(text).toBe('ROW 42y'); + }); + }); +}); diff --git a/packages/kbn-esql-ast/src/pretty_print/pretty_print_one_line.ts b/packages/kbn-esql-ast/src/pretty_print/pretty_print_one_line.ts new file mode 100644 index 0000000000000..94f4afd1acd11 --- /dev/null +++ b/packages/kbn-esql-ast/src/pretty_print/pretty_print_one_line.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { ESQLAstQueryNode, Visitor } from '../visitor'; + +export const prettyPrintOneLine = (query: ESQLAstQueryNode) => { + const visitor = new Visitor() + .on('visitSourceExpression', (ctx) => { + return ctx.node.name; + }) + .on('visitColumnExpression', (ctx) => { + /** + * @todo: Add support for: (1) escaped characters, (2) nested fields. + */ + return ctx.node.name; + }) + .on('visitFunctionCallExpression', (ctx) => { + const node = ctx.node; + let operator = node.name.toUpperCase(); + + switch (node.subtype) { + case 'unary-expression': { + return `${operator} ${ctx.visitArgument(0)}`; + } + case 'postfix-unary-expression': { + return `${ctx.visitArgument(0)} ${operator}`; + } + case 'binary-expression': { + /** @todo Make `operator` printable. */ + switch (operator) { + case 'NOT_LIKE': { + operator = 'NOT LIKE'; + break; + } + case 'NOT_RLIKE': { + operator = 'NOT RLIKE'; + break; + } + } + return `${ctx.visitArgument(0)} ${operator} ${ctx.visitArgument(1)}`; + } + default: { + let args = ''; + + for (const arg of ctx.visitArguments()) { + args += (args ? ', ' : '') + arg; + } + + return `${operator}(${args})`; + } + } + }) + .on('visitLiteralExpression', (ctx) => { + const node = ctx.node; + + switch (node.literalType) { + case 'null': { + return 'NULL'; + } + case 'boolean': { + return String(node.value).toUpperCase() === 'TRUE' ? 'TRUE' : 'FALSE'; + } + case 'param': { + switch (node.paramType) { + case 'named': + case 'positional': + return '?' + node.value; + default: + return '?'; + } + } + case 'string': { + return node.value; + } + default: { + return String(ctx.node.value); + } + } + }) + .on('visitListLiteralExpression', (ctx) => { + let elements = ''; + + for (const arg of ctx.visitElements()) { + elements += (elements ? ', ' : '') + arg; + } + + return `[${elements}]`; + }) + .on('visitTimeIntervalLiteralExpression', (ctx) => { + /** @todo Rename to `fmt`. */ + return ctx.format(); + }) + .on('visitInlineCastExpression', (ctx) => { + /** @todo Add `.fmt()` helper. */ + return `${ctx.visitValue()}::${ctx.node.castType}`; + }) + .on('visitExpression', (ctx) => { + return ctx.node.text ?? '<EXPRESSION>'; + }) + .on('visitCommandOption', (ctx) => { + const option = ctx.node.name.toUpperCase(); + let args = ''; + + for (const arg of ctx.visitArguments()) { + args += (args ? ', ' : '') + arg; + } + + const argsFormatted = args ? ` ${args}` : ''; + const optionFormatted = `${option}${argsFormatted}`; + + return optionFormatted; + }) + .on('visitCommand', (ctx) => { + const cmd = ctx.node.name.toUpperCase(); + let args = ''; + let options = ''; + + for (const source of ctx.visitArguments()) { + args += (args ? ', ' : '') + source; + } + + for (const option of ctx.visitOptions()) { + options += (options ? ' ' : '') + option; + } + + const argsFormatted = args ? ` ${args}` : ''; + const optionsFormatted = options ? ` ${options}` : ''; + const cmdFormatted = `${cmd}${argsFormatted}${optionsFormatted}`; + + return cmdFormatted; + }) + .on('visitQuery', (ctx) => { + let text = ''; + + for (const cmd of ctx.visitCommands()) { + text += (text ? ' | ' : '') + cmd; + } + + return text; + }); + + return visitor.visitQuery(query); +}; diff --git a/packages/kbn-esql-ast/src/visitor/README.md b/packages/kbn-esql-ast/src/visitor/README.md index 71729bf56a0ab..c952c8a34d8d9 100644 --- a/packages/kbn-esql-ast/src/visitor/README.md +++ b/packages/kbn-esql-ast/src/visitor/README.md @@ -55,8 +55,8 @@ expressions `1 + 2`, function call expressions `fn()`, identifier expressions As of this writing, the following expressions are defined: -- Column identifier expression, `{type: "column"}`, like `@timestamp` - Source identifier expression, `{type: "source"}`, like `tsdb_index` +- Column identifier expression, `{type: "column"}`, like `@timestamp` - Function call expression, `{type: "function"}`, like `fn(123)` - Literal expression, `{type: "literal"}`, like `123`, `"hello"` - List literal expression, `{type: "list"}`, like `[1, 2, 3]`, `["a", "b", "c"]`, `[true, false]` diff --git a/packages/kbn-esql-ast/src/visitor/contexts.ts b/packages/kbn-esql-ast/src/visitor/contexts.ts index a7920358e1bab..a9e690b6067d2 100644 --- a/packages/kbn-esql-ast/src/visitor/contexts.ts +++ b/packages/kbn-esql-ast/src/visitor/contexts.ts @@ -81,6 +81,29 @@ export class VisitorContext< } } + public visitArgument( + index: number, + input: ExpressionVisitorInput<Methods> + ): ExpressionVisitorOutput<Methods> { + this.ctx.assertMethodExists('visitExpression'); + + const node = this.node; + + if (!isNodeWithArgs(node)) { + throw new Error('Node does not have arguments'); + } + + let i = 0; + for (const arg of singleItems(node.args)) { + if (i === index) { + return this.visitExpression(arg, input as any); + } + i++; + } + + throw new Error(`Argument at index ${index} not found`); + } + public visitExpression( expressionNode: ESQLAstExpressionNode, input: ExpressionVisitorInput<Methods> @@ -430,14 +453,38 @@ export class ListLiteralExpressionVisitorContext< Methods extends VisitorMethods = VisitorMethods, Data extends SharedData = SharedData, Node extends ESQLList = ESQLList -> extends ExpressionVisitorContext<Methods, Data, Node> {} +> extends ExpressionVisitorContext<Methods, Data, Node> { + public *visitElements( + input: ExpressionVisitorInput<Methods> + ): Iterable<ExpressionVisitorOutput<Methods>> { + this.ctx.assertMethodExists('visitExpression'); + + for (const value of this.node.values) { + yield this.visitExpression(value, input as any); + } + } +} export class TimeIntervalLiteralExpressionVisitorContext< Methods extends VisitorMethods = VisitorMethods, Data extends SharedData = SharedData -> extends ExpressionVisitorContext<Methods, Data, ESQLTimeInterval> {} +> extends ExpressionVisitorContext<Methods, Data, ESQLTimeInterval> { + format(): string { + const node = this.node; + + return `${node.quantity}${node.unit}`; + } +} export class InlineCastExpressionVisitorContext< Methods extends VisitorMethods = VisitorMethods, Data extends SharedData = SharedData -> extends ExpressionVisitorContext<Methods, Data, ESQLInlineCast> {} +> extends ExpressionVisitorContext<Methods, Data, ESQLInlineCast> { + public visitValue(input: ExpressionVisitorInput<Methods>): ExpressionVisitorOutput<Methods> { + this.ctx.assertMethodExists('visitExpression'); + + const value = firstItem([this.node.value])!; + + return this.visitExpression(value, input as any); + } +} diff --git a/packages/kbn-esql-ast/src/visitor/global_visitor_context.ts b/packages/kbn-esql-ast/src/visitor/global_visitor_context.ts index 9cae41f36dde5..d05a4ce326eb7 100644 --- a/packages/kbn-esql-ast/src/visitor/global_visitor_context.ts +++ b/packages/kbn-esql-ast/src/visitor/global_visitor_context.ts @@ -118,11 +118,10 @@ export class GlobalVisitorContext< if (!this.methods.visitStatsCommand) break; return this.visitStatsCommand(parent, commandNode, input as any); } - // TODO: uncomment this when the command is implemented - // case 'inline_stats': { - // if (!this.methods.visitInlineStatsCommand) break; - // return this.visitInlineStatsCommand(parent, commandNode, input as any); - // } + case 'inline_stats': { + if (!this.methods.visitInlineStatsCommand) break; + return this.visitInlineStatsCommand(parent, commandNode, input as any); + } case 'lookup': { if (!this.methods.visitLookupCommand) break; return this.visitLookupCommand(parent, commandNode, input as any); diff --git a/packages/kbn-esql-ast/src/walker/helpers.ts b/packages/kbn-esql-ast/src/walker/helpers.ts new file mode 100644 index 0000000000000..73f0f8d09360c --- /dev/null +++ b/packages/kbn-esql-ast/src/walker/helpers.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 { ESQLProperNode } from '../types'; + +export type NodeMatchTemplateKey<V> = V | V[] | RegExp; +export type NodeMatchTemplate = { + [K in keyof ESQLProperNode]?: NodeMatchTemplateKey<ESQLProperNode[K]>; +}; + +/** + * Creates a predicate function which matches a single AST node against a + * template object. The template object should have the same keys as the + * AST node, and the values should be: + * + * - An array matches if the node key is in the array. + * - A RegExp matches if the node key matches the RegExp. + * - Any other value matches if the node key is triple-equal to the value. + * + * @param template Template from which to create a predicate function. + * @returns A predicate function that matches nodes against the template. + */ +export const templateToPredicate = ( + template: NodeMatchTemplate +): ((node: ESQLProperNode) => boolean) => { + const keys = Object.keys(template) as Array<keyof ESQLProperNode>; + const predicate = (child: ESQLProperNode) => { + for (const key of keys) { + const matcher = template[key]; + if (matcher instanceof Array) { + if (!(matcher as any[]).includes(child[key])) { + return false; + } + } else if (matcher instanceof RegExp) { + if (!matcher.test(String(child[key]))) { + return false; + } + } else if (child[key] !== matcher) { + return false; + } + } + + return true; + }; + + return predicate; +}; diff --git a/packages/kbn-esql-ast/src/walker/walker.test.ts b/packages/kbn-esql-ast/src/walker/walker.test.ts index 9f62c2f07d200..59375b275b162 100644 --- a/packages/kbn-esql-ast/src/walker/walker.test.ts +++ b/packages/kbn-esql-ast/src/walker/walker.test.ts @@ -81,6 +81,24 @@ describe('structurally can walk all nodes', () => { ]); }); + test('"visitAny" can capture command nodes', () => { + const { ast } = getAstAndSyntaxErrors('FROM index | STATS a = 123 | WHERE 123 | LIMIT 10'); + const commands: ESQLCommand[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'command') commands.push(node); + }, + }); + + expect(commands.map(({ name }) => name).sort()).toStrictEqual([ + 'from', + 'limit', + 'stats', + 'where', + ]); + }); + describe('command options', () => { test('can visit command options', () => { const { ast } = getAstAndSyntaxErrors('FROM index METADATA _index'); @@ -93,19 +111,47 @@ describe('structurally can walk all nodes', () => { expect(options.length).toBe(1); expect(options[0].name).toBe('metadata'); }); + + test('"visitAny" can capture an options node', () => { + const { ast } = getAstAndSyntaxErrors('FROM index METADATA _index'); + const options: ESQLCommandOption[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'option') options.push(node); + }, + }); + + expect(options.length).toBe(1); + expect(options[0].name).toBe('metadata'); + }); }); describe('command mode', () => { test('visits "mode" nodes', () => { const { ast } = getAstAndSyntaxErrors('FROM index | ENRICH a:b'); - const options: ESQLCommandMode[] = []; + const modes: ESQLCommandMode[] = []; walk(ast, { - visitCommandMode: (opt) => options.push(opt), + visitCommandMode: (opt) => modes.push(opt), }); - expect(options.length).toBe(1); - expect(options[0].name).toBe('a'); + expect(modes.length).toBe(1); + expect(modes[0].name).toBe('a'); + }); + + test('"visitAny" can capture a mode node', () => { + const { ast } = getAstAndSyntaxErrors('FROM index | ENRICH a:b'); + const modes: ESQLCommandMode[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'mode') modes.push(node); + }, + }); + + expect(modes.length).toBe(1); + expect(modes[0].name).toBe('a'); }); }); @@ -123,6 +169,20 @@ describe('structurally can walk all nodes', () => { expect(sources[0].name).toBe('index'); }); + test('"visitAny" can capture a source node', () => { + const { ast } = getAstAndSyntaxErrors('FROM index'); + const sources: ESQLSource[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'source') sources.push(node); + }, + }); + + expect(sources.length).toBe(1); + expect(sources[0].name).toBe('index'); + }); + test('iterates through all sources', () => { const { ast } = getAstAndSyntaxErrors('METRICS index, index2, index3, index4'); const sources: ESQLSource[] = []; @@ -142,7 +202,7 @@ describe('structurally can walk all nodes', () => { }); describe('columns', () => { - test('can through a single column', () => { + test('can walk through a single column', () => { const query = 'ROW x = 1'; const { ast } = getAstAndSyntaxErrors(query); const columns: ESQLColumn[] = []; @@ -159,6 +219,25 @@ describe('structurally can walk all nodes', () => { ]); }); + test('"visitAny" can capture a column', () => { + const query = 'ROW x = 1'; + const { ast } = getAstAndSyntaxErrors(query); + const columns: ESQLColumn[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'column') columns.push(node); + }, + }); + + expect(columns).toMatchObject([ + { + type: 'column', + name: 'x', + }, + ]); + }); + test('can walk through multiple columns', () => { const query = 'FROM index | STATS a = 123, b = 456'; const { ast } = getAstAndSyntaxErrors(query); @@ -181,6 +260,52 @@ describe('structurally can walk all nodes', () => { }); }); + describe('functions', () => { + test('can walk through functions', () => { + const query = 'FROM a | STATS fn(1), agg(true)'; + const { ast } = getAstAndSyntaxErrors(query); + const nodes: ESQLFunction[] = []; + + walk(ast, { + visitFunction: (node) => nodes.push(node), + }); + + expect(nodes).toMatchObject([ + { + type: 'function', + name: 'fn', + }, + { + type: 'function', + name: 'agg', + }, + ]); + }); + + test('"visitAny" can capture function nodes', () => { + const query = 'FROM a | STATS fn(1), agg(true)'; + const { ast } = getAstAndSyntaxErrors(query); + const nodes: ESQLFunction[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'function') nodes.push(node); + }, + }); + + expect(nodes).toMatchObject([ + { + type: 'function', + name: 'fn', + }, + { + type: 'function', + name: 'agg', + }, + ]); + }); + }); + describe('literals', () => { test('can walk a single literal', () => { const query = 'ROW x = 1'; @@ -301,6 +426,20 @@ describe('structurally can walk all nodes', () => { ]); }); + test('"visitAny" can capture a list literal', () => { + const query = 'ROW x = [1, 2]'; + const { ast } = getAstAndSyntaxErrors(query); + const lists: ESQLList[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'list') lists.push(node); + }, + }); + + expect(lists.length).toBe(1); + }); + test('can walk plain literals inside list literal', () => { const query = 'ROW x = [1, 2] + [3.3]'; const { ast } = getAstAndSyntaxErrors(query); @@ -492,7 +631,6 @@ describe('structurally can walk all nodes', () => { test('can visit time interval nodes', () => { const query = 'FROM index | STATS a = 123 BY 1h'; const { ast } = getAstAndSyntaxErrors(query); - const intervals: ESQLTimeInterval[] = []; walk(ast, { @@ -507,6 +645,43 @@ describe('structurally can walk all nodes', () => { }, ]); }); + + test('"visitAny" can capture time interval expressions', () => { + const query = 'FROM index | STATS a = 123 BY 1h'; + const { ast } = getAstAndSyntaxErrors(query); + const intervals: ESQLTimeInterval[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'timeInterval') intervals.push(node); + }, + }); + + expect(intervals).toMatchObject([ + { + type: 'timeInterval', + quantity: 1, + unit: 'h', + }, + ]); + }); + + test('"visitAny" does not capture time interval node if type-specific callback provided', () => { + const query = 'FROM index | STATS a = 123 BY 1h'; + const { ast } = getAstAndSyntaxErrors(query); + const intervals1: ESQLTimeInterval[] = []; + const intervals2: ESQLTimeInterval[] = []; + + walk(ast, { + visitTimeIntervalLiteral: (node) => intervals1.push(node), + visitAny: (node) => { + if (node.type === 'timeInterval') intervals2.push(node); + }, + }); + + expect(intervals1.length).toBe(1); + expect(intervals2.length).toBe(0); + }); }); describe('cast expression', () => { @@ -532,6 +707,30 @@ describe('structurally can walk all nodes', () => { }, ]); }); + + test('"visitAny" can capture cast expression', () => { + const query = 'FROM index | STATS a = 123::integer'; + const { ast } = getAstAndSyntaxErrors(query); + const casts: ESQLInlineCast[] = []; + + walk(ast, { + visitAny: (node) => { + if (node.type === 'inlineCast') casts.push(node); + }, + }); + + expect(casts).toMatchObject([ + { + type: 'inlineCast', + castType: 'integer', + value: { + type: 'literal', + literalType: 'integer', + value: 123, + }, + }, + ]); + }); }); }); }); @@ -576,7 +775,7 @@ describe('Walker.commands()', () => { }); }); -describe('Walker.params', () => { +describe('Walker.params()', () => { test('can collect all params', () => { const query = 'ROW x = ?'; const { ast } = getAstAndSyntaxErrors(query); @@ -613,10 +812,195 @@ describe('Walker.params', () => { }); }); +describe('Walker.find()', () => { + test('can find a bucket() function', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const fn = Walker.find( + getAstAndSyntaxErrors(query).ast!, + (node) => node.type === 'function' && node.name === 'bucket' + ); + + expect(fn).toMatchObject({ + type: 'function', + name: 'bucket', + }); + }); + + test('finds the first "fn" function', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const fn = Walker.find( + getAstAndSyntaxErrors(query).ast!, + (node) => node.type === 'function' && node.name === 'fn' + ); + + expect(fn).toMatchObject({ + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 1, + }, + ], + }); + }); +}); + +describe('Walker.findAll()', () => { + test('find all "fn" functions', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const list = Walker.findAll( + getAstAndSyntaxErrors(query).ast!, + (node) => node.type === 'function' && node.name === 'fn' + ); + + expect(list).toMatchObject([ + { + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 1, + }, + ], + }, + { + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 2, + }, + ], + }, + ]); + }); +}); + +describe('Walker.match()', () => { + test('can find a bucket() function', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const fn = Walker.match(getAstAndSyntaxErrors(query).ast!, { + type: 'function', + name: 'bucket', + }); + + expect(fn).toMatchObject({ + type: 'function', + name: 'bucket', + }); + }); + + test('finds the first "fn" function', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const fn = Walker.match(getAstAndSyntaxErrors(query).ast!, { type: 'function', name: 'fn' }); + + expect(fn).toMatchObject({ + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 1, + }, + ], + }); + }); +}); + +describe('Walker.matchAll()', () => { + test('find all "fn" functions', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const list = Walker.matchAll(getAstAndSyntaxErrors(query).ast!, { + type: 'function', + name: 'fn', + }); + + expect(list).toMatchObject([ + { + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 1, + }, + ], + }, + { + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 2, + }, + ], + }, + ]); + }); + + test('find all "fn" and "agg" functions', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const list = Walker.matchAll(getAstAndSyntaxErrors(query).ast!, { + type: 'function', + name: ['fn', 'agg'], + }); + + expect(list).toMatchObject([ + { + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 1, + }, + ], + }, + { + type: 'function', + name: 'fn', + args: [ + { + type: 'literal', + value: 2, + }, + ], + }, + { + type: 'function', + name: 'agg', + }, + ]); + }); + + test('find all functions which start with "b" or "a"', () => { + const query = 'FROM b | STATS var0 = bucket(bytes, 1 hour), fn(1), fn(2), agg(true)'; + const list = Walker.matchAll(getAstAndSyntaxErrors(query).ast!, { + type: 'function', + name: /^a|b/i, + }); + + expect(list).toMatchObject([ + { + type: 'function', + name: 'bucket', + }, + { + type: 'function', + name: 'agg', + }, + ]); + }); +}); + describe('Walker.hasFunction()', () => { test('can find assignment expression', () => { - const query1 = 'METRICS source bucket(bytes, 1 hour)'; - const query2 = 'METRICS source var0 = bucket(bytes, 1 hour)'; + const query1 = 'FROM a | STATS bucket(bytes, 1 hour)'; + const query2 = 'FROM b | STATS var0 = bucket(bytes, 1 hour)'; const has1 = Walker.hasFunction(getAstAndSyntaxErrors(query1).ast!, '='); const has2 = Walker.hasFunction(getAstAndSyntaxErrors(query2).ast!, '='); diff --git a/packages/kbn-esql-ast/src/walker/walker.ts b/packages/kbn-esql-ast/src/walker/walker.ts index 20e052d211fe1..e6ed54517435e 100644 --- a/packages/kbn-esql-ast/src/walker/walker.ts +++ b/packages/kbn-esql-ast/src/walker/walker.ts @@ -19,11 +19,13 @@ import type { ESQLList, ESQLLiteral, ESQLParamLiteral, + ESQLProperNode, ESQLSingleAstItem, ESQLSource, ESQLTimeInterval, ESQLUnknownItem, } from '../types'; +import { NodeMatchTemplate, templateToPredicate } from './helpers'; type Node = ESQLAstNode | ESQLAstNode[]; @@ -40,6 +42,13 @@ export interface WalkerOptions { visitTimeIntervalLiteral?: (node: ESQLTimeInterval) => void; visitInlineCast?: (node: ESQLInlineCast) => void; visitUnknown?: (node: ESQLUnknownItem) => void; + + /** + * Called for any node type that does not have a specific visitor. + * + * @param node Any valid AST node. + */ + visitAny?: (node: ESQLProperNode) => void; } export type WalkerAstNode = ESQLAstNode | ESQLAstNode[]; @@ -102,6 +111,82 @@ export class Walker { return params; }; + /** + * Finds and returns the first node that matches the search criteria. + * + * @param node AST node to start the search from. + * @param predicate A function that returns true if the node matches the search criteria. + * @returns The first node that matches the search criteria. + */ + public static readonly find = ( + node: WalkerAstNode, + predicate: (node: ESQLProperNode) => boolean + ): ESQLProperNode | undefined => { + let found: ESQLProperNode | undefined; + Walker.walk(node, { + visitAny: (child) => { + if (!found && predicate(child)) { + found = child; + } + }, + }); + return found; + }; + + /** + * Finds and returns all nodes that match the search criteria. + * + * @param node AST node to start the search from. + * @param predicate A function that returns true if the node matches the search criteria. + * @returns All nodes that match the search criteria. + */ + public static readonly findAll = ( + node: WalkerAstNode, + predicate: (node: ESQLProperNode) => boolean + ): ESQLProperNode[] => { + const list: ESQLProperNode[] = []; + Walker.walk(node, { + visitAny: (child) => { + if (predicate(child)) { + list.push(child); + } + }, + }); + return list; + }; + + /** + * Matches a single node against a template object. Returns the first node + * that matches the template. + * + * @param node AST node to match against the template. + * @param template Template object to match against the node. + * @returns The first node that matches the template + */ + public static readonly match = ( + node: WalkerAstNode, + template: NodeMatchTemplate + ): ESQLProperNode | undefined => { + const predicate = templateToPredicate(template); + return Walker.find(node, predicate); + }; + + /** + * Matches all nodes against a template object. Returns all nodes that match + * the template. + * + * @param node AST node to match against the template. + * @param template Template object to match against the node. + * @returns All nodes that match the template + */ + public static readonly matchAll = ( + node: WalkerAstNode, + template: NodeMatchTemplate + ): ESQLProperNode[] => { + const predicate = templateToPredicate(template); + return Walker.findAll(node, predicate); + }; + /** * Finds the first function that matches the predicate. * @@ -161,7 +246,8 @@ export class Walker { } public walkCommand(node: ESQLAstCommand): void { - this.options.visitCommand?.(node); + const { options } = this; + (options.visitCommand ?? options.visitAny)?.(node); switch (node.name) { default: { this.walk(node.args); @@ -171,7 +257,8 @@ export class Walker { } public walkOption(node: ESQLCommandOption): void { - this.options.visitCommandOption?.(node); + const { options } = this; + (options.visitCommandOption ?? options.visitAny)?.(node); for (const child of node.args) { this.walkAstItem(child); } @@ -188,11 +275,13 @@ export class Walker { } public walkMode(node: ESQLCommandMode): void { - this.options.visitCommandMode?.(node); + const { options } = this; + (options.visitCommandMode ?? options.visitAny)?.(node); } public walkListLiteral(node: ESQLList): void { - this.options.visitListLiteral?.(node); + const { options } = this; + (options.visitListLiteral ?? options.visitAny)?.(node); for (const value of node.values) { this.walkAstItem(value); } @@ -215,11 +304,11 @@ export class Walker { break; } case 'source': { - options.visitSource?.(node); + (options.visitSource ?? options.visitAny)?.(node); break; } case 'column': { - options.visitColumn?.(node); + (options.visitColumn ?? options.visitAny)?.(node); break; } case 'literal': { @@ -231,22 +320,23 @@ export class Walker { break; } case 'timeInterval': { - options.visitTimeIntervalLiteral?.(node); + (options.visitTimeIntervalLiteral ?? options.visitAny)?.(node); break; } case 'inlineCast': { - options.visitInlineCast?.(node); + (options.visitInlineCast ?? options.visitAny)?.(node); break; } case 'unknown': { - options.visitUnknown?.(node); + (options.visitUnknown ?? options.visitAny)?.(node); break; } } } public walkFunction(node: ESQLFunction): void { - this.options.visitFunction?.(node); + const { options } = this; + (options.visitFunction ?? options.visitAny)?.(node); const args = node.args; const length = args.length; for (let i = 0; i < length; i++) { diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts index cbee9d00e8928..2f4a9c7cf4c33 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts @@ -25,7 +25,7 @@ const aliasTable: Record<string, string[]> = { const aliases = new Set(Object.values(aliasTable).flat()); const evalSupportedCommandsAndOptions = { - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], }; @@ -61,7 +61,7 @@ const validateLogFunctions = `(fnDef: ESQLFunction) => { // do not really care here about the base and field // just need to check both values are not negative for (const arg of fnDef.args) { - if (isLiteralItem(arg) && arg.value < 0) { + if (isLiteralItem(arg) && Number(arg.value) < 0) { messages.push({ type: 'warning' as const, code: 'logOfNegativeValue', @@ -213,12 +213,14 @@ const functionEnrichments: Record<string, RecursivePartial<FunctionDefinition>> ], }, mv_sort: { - signatures: new Array(6).fill({ + signatures: new Array(9).fill({ params: [{}, { literalOptions: ['asc', 'desc'] }], }), }, }; +const convertDateTime = (s: string) => (s === 'datetime' ? 'date' : s); + /** * Builds a function definition object from a row of the "meta functions" table * @param {Array<any>} value — the row of the "meta functions" table, corresponding to a single function definition @@ -239,10 +241,10 @@ function getFunctionDefinition(ESFunctionDefinition: Record<string, any>): Funct ...signature, params: signature.params.map((param: any) => ({ ...param, - type: param.type, + type: convertDateTime(param.type), description: undefined, })), - returnType: signature.returnType, + returnType: convertDateTime(signature.returnType), variadic: undefined, // we don't support variadic property minParams: signature.variadic ? signature.params.filter((param: any) => !param.optional).length diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts index d3497115aecf9..f431d3cddb2c5 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts @@ -18,16 +18,17 @@ import { getFunctionSignatures } from '../src/definitions/helpers'; import { timeUnits } from '../src/definitions/literals'; import { nonNullable } from '../src/shared/helpers'; import { - SupportedFieldType, + SupportedDataType, FunctionDefinition, - supportedFieldTypes, - isSupportedFieldType, + dataTypes, + isSupportedDataType, + fieldTypes, } from '../src/definitions/types'; import { FUNCTION_DESCRIBE_BLOCK_NAME } from '../src/validation/function_describe_block_name'; import { getMaxMinNumberOfParams } from '../src/validation/helpers'; import { ESQL_NUMBER_TYPES, isNumericType, isStringType } from '../src/shared/esql_types'; -export const fieldNameFromType = (type: SupportedFieldType) => `${camelCase(type)}Field`; +export const fieldNameFromType = (type: SupportedDataType) => `${camelCase(type)}Field`; function main() { const testCasesByFunction: Map<string, Map<string, string[]>> = new Map(); @@ -301,8 +302,8 @@ function generateWhereCommandTestsForEvalFunction( // TODO: not sure why there's this constraint... const supportedFunction = signatures.some( ({ returnType, params }) => - [...ESQL_NUMBER_TYPES, 'string'].includes(returnType) && - params.every(({ type }) => [...ESQL_NUMBER_TYPES, 'string'].includes(type)) + [...ESQL_NUMBER_TYPES, 'string'].includes(returnType as string) && + params.every(({ type }) => [...ESQL_NUMBER_TYPES, 'string'].includes(type as string)) ); if (!supportedFunction) { @@ -312,7 +313,7 @@ function generateWhereCommandTestsForEvalFunction( const supportedSignatures = signatures.filter(({ returnType }) => // TODO — not sure why the tests have this limitation... seems like any type // that can be part of a boolean expression should be allowed in a where clause - [...ESQL_NUMBER_TYPES, 'string'].includes(returnType) + [...ESQL_NUMBER_TYPES, 'string'].includes(returnType as string) ); for (const { params, returnType, ...restSign } of supportedSignatures) { const correctMapping = getFieldMapping(params); @@ -905,7 +906,7 @@ function generateStatsCommandTestsForGroupingFunction( fieldReplacedType // if a param of type time_literal or chrono_literal it will always be a literal // so no way to test the constantOnly thing - .filter((type) => !['time_literal', 'chrono_literal'].includes(type)) + .filter((type) => !['time_literal'].includes(type as string)) .map((type) => `Argument of [${name}] must be a constant, received [${type}Field]`) ); } @@ -965,7 +966,7 @@ function generateSortCommandTestsForAggFunction( const generateSortCommandTestsForGroupingFunction = generateSortCommandTestsForAggFunction; -const fieldTypesToConstants: Record<SupportedFieldType, string> = { +const fieldTypesToConstants: Record<SupportedDataType, string> = { text: '"a"', keyword: '"a"', double: '5.5', @@ -984,14 +985,20 @@ const fieldTypesToConstants: Record<SupportedFieldType, string> = { geo_shape: 'to_geoshape("POINT (30 10)")', cartesian_point: 'to_cartesianpoint("POINT (30 10)")', cartesian_shape: 'to_cartesianshape("POINT (30 10)")', + null: 'NULL', + time_duration: '1 day', + // the following are never supplied + // by the ES function definitions. Just making types happy + time_literal: '1 day', + unsupported: '', }; -const supportedTypesAndFieldNames = supportedFieldTypes.map((type) => ({ +const supportedTypesAndFieldNames = fieldTypes.map((type) => ({ name: fieldNameFromType(type), type, })); -const supportedTypesAndConstants = supportedFieldTypes.map((type) => ({ +const supportedTypesAndConstants = dataTypes.map((type) => ({ name: fieldTypesToConstants[type], type, })); @@ -1029,7 +1036,7 @@ const toCartesianShapeSignature = evalFunctionDefinitions.find( const toVersionSignature = evalFunctionDefinitions.find(({ name }) => name === 'to_version')!; // We don't have full list for long, unsigned_long, etc. -const nestedFunctions: Record<SupportedFieldType, string> = { +const nestedFunctions: Record<SupportedDataType, string> = { double: prepareNestedFunction(toDoubleSignature), integer: prepareNestedFunction(toInteger), text: prepareNestedFunction(toStringSignature), @@ -1046,7 +1053,7 @@ const nestedFunctions: Record<SupportedFieldType, string> = { }; function getFieldName( - typeString: SupportedFieldType, + typeString: SupportedDataType, { useNestedFunction, isStats }: { useNestedFunction: boolean; isStats: boolean } ) { if (useNestedFunction && isStats) { @@ -1082,7 +1089,7 @@ function tweakSignatureForRowCommand(signature: string): string { */ let ret = signature; for (const [type, value] of Object.entries(fieldTypesToConstants)) { - ret = ret.replace(new RegExp(fieldNameFromType(type as SupportedFieldType), 'g'), value); + ret = ret.replace(new RegExp(fieldNameFromType(type as SupportedDataType), 'g'), value); } return ret; } @@ -1101,8 +1108,8 @@ function getFieldMapping( }; return params.map(({ name: _name, type, constantOnly, literalOptions, ...rest }) => { - const typeString: string = type; - if (isSupportedFieldType(typeString)) { + const typeString: string = type as string; + if (isSupportedDataType(typeString)) { if (useLiterals && literalOptions) { return { name: `"${literalOptions[0]}"`, @@ -1146,7 +1153,7 @@ function generateIncorrectlyTypedParameters( name: string, signatures: FunctionDefinition['signatures'], currentParams: FunctionDefinition['signatures'][number]['params'], - availableFields: Array<{ name: string; type: SupportedFieldType }> + availableFields: Array<{ name: string; type: SupportedDataType }> ) { const literalValues = { string: `"a"`, @@ -1167,7 +1174,7 @@ function generateIncorrectlyTypedParameters( if (type !== 'any') { // try to find an unacceptable field - const unacceptableField: { name: string; type: SupportedFieldType } | undefined = + const unacceptableField: { name: string; type: SupportedDataType } | undefined = availableFields // sort to make the test deterministic .sort((a, b) => a.type.localeCompare(b.type)) @@ -1187,7 +1194,7 @@ function generateIncorrectlyTypedParameters( } // failed to find a bad field... they must all be acceptable - const acceptableField: { name: string; type: SupportedFieldType } | undefined = + const acceptableField: { name: string; type: SupportedDataType } | undefined = type === 'any' ? availableFields[0] : availableFields.find(({ type: fieldType }) => fieldType === type); diff --git a/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts index 5f24d86e718bc..273679ab1f267 100644 --- a/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts @@ -7,22 +7,27 @@ */ import { camelCase } from 'lodash'; -import { supportedFieldTypes } from '../definitions/types'; +import { ESQLRealField } from '../validation/types'; +import { fieldTypes } from '../definitions/types'; -export const fields = [ - ...supportedFieldTypes.map((type) => ({ name: `${camelCase(type)}Field`, type })), +export const fields: ESQLRealField[] = [ + ...fieldTypes + .map((type) => ({ name: `${camelCase(type)}Field`, type })) + .filter((f) => f.type !== 'unsupported'), { name: 'any#Char$Field', type: 'double' }, { name: 'kubernetes.something.something', type: 'double' }, { name: '@timestamp', type: 'date' }, ]; -export const enrichFields = [ +export const enrichFields: ESQLRealField[] = [ { name: 'otherField', type: 'text' }, { name: 'yetAnotherField', type: 'double' }, ]; // eslint-disable-next-line @typescript-eslint/naming-convention -export const unsupported_field = [{ name: 'unsupported_field', type: 'unsupported' }]; +export const unsupported_field: ESQLRealField[] = [ + { name: 'unsupported_field', type: 'unsupported' }, +]; export const indexes = [ 'a_index', @@ -58,7 +63,8 @@ export function getCallbackMocks() { return unsupported_field; } if (/dissect|grok/.test(query)) { - return [{ name: 'firstWord', type: 'text' }]; + const field: ESQLRealField = { name: 'firstWord', type: 'text' }; + return [field]; } return fields; }), diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts index 3752fad6c580f..911f8760f60b7 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.from.test.ts @@ -14,6 +14,9 @@ const visibleIndices = indexes .map(({ name, suggestedAs }) => suggestedAs || name) .sort(); +const addTrailingSpace = (strings: string[], predicate: (s: string) => boolean = (_s) => true) => + strings.map((string) => (predicate(string) ? `${string} ` : string)); + const metadataFields = [...METADATA_FIELDS].sort(); describe('autocomplete.suggest', () => { @@ -33,17 +36,17 @@ describe('autocomplete.suggest', () => { test('suggests visible indices on space', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from /', visibleIndices); - await assertSuggestions('FROM /', visibleIndices); - await assertSuggestions('from /index', visibleIndices); + await assertSuggestions('from /', addTrailingSpace(visibleIndices)); + await assertSuggestions('FROM /', addTrailingSpace(visibleIndices)); + await assertSuggestions('from /index', addTrailingSpace(visibleIndices)); }); test('suggests visible indices on comma', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('FROM a,/', visibleIndices); - await assertSuggestions('FROM a, /', visibleIndices); - await assertSuggestions('from *,/', visibleIndices); + await assertSuggestions('FROM a,/', addTrailingSpace(visibleIndices)); + await assertSuggestions('FROM a, /', addTrailingSpace(visibleIndices)); + await assertSuggestions('from *,/', addTrailingSpace(visibleIndices)); }); test('can suggest integration data sources', async () => { @@ -52,17 +55,21 @@ describe('autocomplete.suggest', () => { .filter(({ hidden }) => !hidden) .map(({ name, suggestedAs }) => suggestedAs || name) .sort(); + const expectedSuggestions = addTrailingSpace( + visibleDataSources, + (s) => !integrations.find(({ name }) => name === s) + ); const { assertSuggestions, callbacks } = await setup(); const cb = { ...callbacks, getSources: jest.fn().mockResolvedValue(dataSources), }; - assertSuggestions('from /', visibleDataSources, { callbacks: cb }); - assertSuggestions('FROM /', visibleDataSources, { callbacks: cb }); - assertSuggestions('FROM a,/', visibleDataSources, { callbacks: cb }); - assertSuggestions('from a, /', visibleDataSources, { callbacks: cb }); - assertSuggestions('from *,/', visibleDataSources, { callbacks: cb }); + await assertSuggestions('from /', expectedSuggestions, { callbacks: cb }); + await assertSuggestions('FROM /', expectedSuggestions, { callbacks: cb }); + await assertSuggestions('FROM a,/', expectedSuggestions, { callbacks: cb }); + await assertSuggestions('from a, /', expectedSuggestions, { callbacks: cb }); + await assertSuggestions('from *,/', expectedSuggestions, { callbacks: cb }); }); }); @@ -71,7 +78,7 @@ describe('autocomplete.suggest', () => { test('on <kbd>SPACE</kbd> without comma ",", suggests adding metadata', async () => { const { assertSuggestions } = await setup(); - const expected = ['METADATA $0', ',', '|'].sort(); + const expected = ['METADATA $0', ',', '| '].sort(); await assertSuggestions('from a, b /', expected); }); @@ -86,10 +93,10 @@ describe('autocomplete.suggest', () => { test('on <kbd>SPACE</kbd> after "METADATA" column suggests command and pipe operators', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a, b [metadata _index /]', [',', '|']); - await assertSuggestions('from a, b metadata _index /', [',', '|']); - await assertSuggestions('from a, b metadata _index, _source /', [',', '|']); - await assertSuggestions(`from a, b metadata ${METADATA_FIELDS.join(', ')} /`, ['|']); + await assertSuggestions('from a, b [metadata _index /]', [',', '| ']); + await assertSuggestions('from a, b metadata _index /', [',', '| ']); + await assertSuggestions('from a, b metadata _index, _source /', [',', '| ']); + await assertSuggestions(`from a, b metadata ${METADATA_FIELDS.join(', ')} /`, ['| ']); }); test('filters out already used metadata fields', async () => { diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts index 88fd654a83453..cdf4a6239a9ab 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts @@ -9,8 +9,6 @@ import { ESQL_COMMON_NUMERIC_TYPES, ESQL_NUMBER_TYPES } from '../../shared/esql_types'; import { setup, getFunctionSignaturesByReturnType, getFieldNamesByType } from './helpers'; -const ESQL_NUMERIC_TYPES = ESQL_NUMBER_TYPES as unknown as string[]; - const allAggFunctions = getFunctionSignaturesByReturnType('stats', 'any', { agg: true, }); @@ -45,7 +43,7 @@ describe('autocomplete.suggest', () => { describe('... <aggregates> ...', () => { test('lists possible aggregations on space after command', async () => { const { assertSuggestions } = await setup(); - const expected = ['var0 =', ...allAggFunctions, ...allEvaFunctions]; + const expected = ['var0 = ', ...allAggFunctions, ...allEvaFunctions]; await assertSuggestions('from a | stats /', expected); await assertSuggestions('FROM a | STATS /', expected); @@ -60,14 +58,14 @@ describe('autocomplete.suggest', () => { test('on space after aggregate field', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats a=min(b) /', ['BY $0', ',', '|']); + await assertSuggestions('from a | stats a=min(b) /', ['BY $0', ',', '| ']); }); test('on space after aggregate field with comma', async () => { const { assertSuggestions } = await setup(); await assertSuggestions('from a | stats a=max(b), /', [ - 'var0 =', + 'var0 = ', ...allAggFunctions, ...allEvaFunctions, ]); @@ -78,57 +76,57 @@ describe('autocomplete.suggest', () => { await assertSuggestions('from a | stats by bucket(/', [ ...getFieldNamesByType([...ESQL_COMMON_NUMERIC_TYPES, 'date']).map( - (field) => `${field},` + (field) => `${field}, ` ), ...getFunctionSignaturesByReturnType('eval', ['date', ...ESQL_COMMON_NUMERIC_TYPES], { scalar: true, }).map((s) => ({ ...s, text: `${s.text},` })), ]); - + const roundParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; await assertSuggestions('from a | stats round(/', [ - ...getFunctionSignaturesByReturnType('stats', ESQL_NUMERIC_TYPES, { + ...getFunctionSignaturesByReturnType('stats', roundParameterTypes, { agg: true, grouping: true, }), - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] ), ]); await assertSuggestions('from a | stats round(round(/', [ - ...getFunctionSignaturesByReturnType('stats', ESQL_NUMERIC_TYPES, { agg: true }), - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('stats', roundParameterTypes, { agg: true }), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + ESQL_NUMBER_TYPES, { scalar: true }, undefined, ['round'] ), ]); await assertSuggestions('from a | stats avg(round(/', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + ESQL_NUMBER_TYPES, { scalar: true }, undefined, ['round'] ), ]); await assertSuggestions('from a | stats avg(/', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_NUMBER_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_NUMBER_TYPES, { scalar: true }), ]); await assertSuggestions('from a | stats round(avg(/', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(ESQL_NUMBER_TYPES), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + ESQL_NUMBER_TYPES, { scalar: true }, undefined, ['round'] @@ -142,7 +140,7 @@ describe('autocomplete.suggest', () => { ...getFieldNamesByType([...ESQL_COMMON_NUMERIC_TYPES, 'date', 'boolean', 'ip']), ...getFunctionSignaturesByReturnType( 'stats', - [...ESQL_COMMON_NUMERIC_TYPES, 'date', 'boolean', 'ip'], + [...ESQL_COMMON_NUMERIC_TYPES, 'date', 'date_period', 'boolean', 'ip'], { scalar: true, } @@ -158,7 +156,7 @@ describe('autocomplete.suggest', () => { const { assertSuggestions } = await setup(); await assertSuggestions('from a | stats avg(b/) by stringField', [ - ...getFieldNamesByType('double'), + ...getFieldNamesByType(ESQL_NUMBER_TYPES), ...getFunctionSignaturesByReturnType( 'eval', ['double', 'integer', 'long', 'unsigned_long'], @@ -172,21 +170,21 @@ describe('autocomplete.suggest', () => { test('when typing right paren', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats a = min(b)/ | sort b', ['BY $0', ',', '|']); + await assertSuggestions('from a | stats a = min(b)/ | sort b', ['BY $0', ',', '| ']); }); test('increments suggested variable name counter', async () => { const { assertSuggestions } = await setup(); await assertSuggestions('from a | eval var0=round(b), var1=round(c) | stats /', [ - 'var2 =', + 'var2 = ', ...allAggFunctions, 'var0', 'var1', ...allEvaFunctions, ]); await assertSuggestions('from a | stats var0=min(b),var1=c,/', [ - 'var2 =', + 'var2 = ', ...allAggFunctions, ...allEvaFunctions, ]); @@ -197,8 +195,8 @@ describe('autocomplete.suggest', () => { test('on space after "BY" keyword', async () => { const { assertSuggestions } = await setup(); const expected = [ - 'var0 =', - ...getFieldNamesByType('any'), + 'var0 = ', + ...getFieldNamesByType('any').map((field) => `${field} `), ...allEvaFunctions, ...allGroupingFunctions, ]; @@ -211,26 +209,27 @@ describe('autocomplete.suggest', () => { test('on space after grouping field', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats a=c by d /', [',', '|']); + await assertSuggestions('from a | stats a=c by d /', [',', '| ']); }); test('after comma "," in grouping fields', async () => { const { assertSuggestions } = await setup(); + const fields = getFieldNamesByType('any').map((field) => `${field} `); await assertSuggestions('from a | stats a=c by d, /', [ - 'var0 =', - ...getFieldNamesByType('any'), + 'var0 = ', + ...fields, ...allEvaFunctions, ...allGroupingFunctions, ]); await assertSuggestions('from a | stats a=min(b),/', [ - 'var0 =', + 'var0 = ', ...allAggFunctions, ...allEvaFunctions, ]); await assertSuggestions('from a | stats avg(b) by c, /', [ - 'var0 =', - ...getFieldNamesByType('any'), + 'var0 = ', + ...fields, ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ...allGroupingFunctions, ]); @@ -251,12 +250,12 @@ describe('autocomplete.suggest', () => { ...allGroupingFunctions, ]); await assertSuggestions('from a | stats avg(b) by var0 = /', [ - ...getFieldNamesByType('any'), + ...getFieldNamesByType('any').map((field) => `${field} `), ...allEvaFunctions, ...allGroupingFunctions, ]); await assertSuggestions('from a | stats avg(b) by c, var0 = /', [ - ...getFieldNamesByType('any'), + ...getFieldNamesByType('any').map((field) => `${field} `), ...allEvaFunctions, ...allGroupingFunctions, ]); @@ -265,11 +264,11 @@ describe('autocomplete.suggest', () => { test('on space after expression right hand side operand', async () => { const { assertSuggestions } = await setup(); - await assertSuggestions('from a | stats avg(b) by doubleField % 2 /', [',', '|']); + await assertSuggestions('from a | stats avg(b) by doubleField % 2 /', [',', '| ']); await assertSuggestions( 'from a | stats var0 = AVG(doubleField) BY var1 = BUCKET(dateField, 1 day)/', - [',', '|', '+ $0', '- $0'] + [',', '| ', '+ $0', '- $0'] ); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts index 6600ffdbaf1d8..4c4ad9902ac8c 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts @@ -18,6 +18,14 @@ import type { ESQLCallbacks } from '../../shared/types'; import type { EditorContext, SuggestionRawDefinition } from '../types'; import { TIME_SYSTEM_PARAMS } from '../factories'; import { getFunctionSignatures } from '../../definitions/helpers'; +import { ESQLRealField } from '../../validation/types'; +import { + FieldType, + fieldTypes, + FunctionParameterType, + FunctionReturnType, + SupportedDataType, +} from '../../definitions/types'; export interface Integration { name: string; @@ -38,18 +46,8 @@ export const TIME_PICKER_SUGGESTION: PartialSuggestionWithText = { export const triggerCharacters = [',', '(', '=', ' ']; -export const fields: Array<{ name: string; type: string; suggestedAs?: string }> = [ - ...[ - 'string', - 'double', - 'date', - 'boolean', - 'ip', - 'geo_point', - 'geo_shape', - 'cartesian_point', - 'cartesian_shape', - ].map((type) => ({ +export const fields: Array<ESQLRealField & { suggestedAs?: string }> = [ + ...fieldTypes.map((type) => ({ name: `${camelCase(type)}Field`, type, })), @@ -124,7 +122,7 @@ export const policies = [ */ export function getFunctionSignaturesByReturnType( command: string, - _expectedReturnType: string | string[], + _expectedReturnType: Readonly<FunctionReturnType | 'any' | Array<FunctionReturnType | 'any'>>, { agg, grouping, @@ -140,7 +138,7 @@ export function getFunctionSignaturesByReturnType( builtin?: boolean; skipAssign?: boolean; } = {}, - paramsTypes?: string[], + paramsTypes?: Readonly<FunctionParameterType[]>, ignored?: string[], option?: string ): PartialSuggestionWithText[] { @@ -177,7 +175,7 @@ export function getFunctionSignaturesByReturnType( } const filteredByReturnType = signatures.filter( ({ returnType }) => - expectedReturnType.includes('any') || expectedReturnType.includes(returnType) + expectedReturnType.includes('any') || expectedReturnType.includes(returnType as string) ); if (!filteredByReturnType.length) { return false; @@ -226,14 +224,16 @@ export function getFunctionSignaturesByReturnType( }); } -export function getFieldNamesByType(_requestedType: string | string[]) { +export function getFieldNamesByType( + _requestedType: Readonly<FieldType | 'any' | Array<FieldType | 'any'>> +) { const requestedType = Array.isArray(_requestedType) ? _requestedType : [_requestedType]; return fields .filter(({ type }) => requestedType.includes('any') || requestedType.includes(type)) .map(({ name, suggestedAs }) => suggestedAs || name); } -export function getLiteralsByType(_type: string | string[]) { +export function getLiteralsByType(_type: SupportedDataType | SupportedDataType[]) { const type = Array.isArray(_type) ? _type : [_type]; if (type.includes('time_literal')) { // return only singular @@ -242,13 +242,13 @@ export function getLiteralsByType(_type: string | string[]) { return []; } -export function getDateLiteralsByFieldType(_requestedType: string | string[]) { +export function getDateLiteralsByFieldType(_requestedType: FieldType | FieldType[]) { const requestedType = Array.isArray(_requestedType) ? _requestedType : [_requestedType]; return requestedType.includes('date') ? [TIME_PICKER_SUGGESTION, ...TIME_SYSTEM_PARAMS] : []; } export function createCustomCallbackMocks( - customFields?: Array<{ name: string; type: string }>, + customFields?: ESQLRealField[], customSources?: Array<{ name: string; hidden: boolean }>, customPolicies?: Array<{ name: string; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts index d9c958b5bd4f7..e07737ea60897 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts @@ -9,11 +9,21 @@ import { suggest } from './autocomplete'; import { evalFunctionDefinitions } from '../definitions/functions'; import { timeUnitsToSuggest } from '../definitions/literals'; -import { commandDefinitions } from '../definitions/commands'; -import { getSafeInsertText, getUnitDuration, TRIGGER_SUGGESTION_COMMAND } from './factories'; +import { commandDefinitions as unmodifiedCommandDefinitions } from '../definitions/commands'; +import { + getSafeInsertText, + getUnitDuration, + TIME_SYSTEM_PARAMS, + TRIGGER_SUGGESTION_COMMAND, +} from './factories'; import { camelCase, partition } from 'lodash'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; -import { FunctionParameter, FunctionReturnType } from '../definitions/types'; +import { + FunctionParameter, + isFieldType, + isSupportedDataType, + SupportedDataType, +} from '../definitions/types'; import { getParamAtPosition } from './helper'; import { nonNullable } from '../shared/helpers'; import { @@ -26,17 +36,16 @@ import { createCompletionContext, getPolicyFields, PartialSuggestionWithText, + TIME_PICKER_SUGGESTION, } from './__tests__/helpers'; import { METADATA_FIELDS } from '../shared/constants'; -import { - ESQL_COMMON_NUMERIC_TYPES as UNCASTED_ESQL_COMMON_NUMERIC_TYPES, - ESQL_NUMBER_TYPES, -} from '../shared/esql_types'; +import { ESQL_COMMON_NUMERIC_TYPES, ESQL_STRING_TYPES } from '../shared/esql_types'; -const ESQL_NUMERIC_TYPES = ESQL_NUMBER_TYPES as unknown as string[]; -const ESQL_COMMON_NUMERIC_TYPES = - UNCASTED_ESQL_COMMON_NUMERIC_TYPES as unknown as FunctionReturnType[]; +const roundParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; +const powParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; +const log10ParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; +const commandDefinitions = unmodifiedCommandDefinitions.filter(({ hidden }) => !hidden); describe('autocomplete', () => { type TestArgs = [ string, @@ -107,7 +116,7 @@ describe('autocomplete', () => { }, }); - const sourceCommands = ['row', 'from', 'show']; + const sourceCommands = ['row', 'from', 'show', 'metrics']; describe('New command', () => { testSuggestions( @@ -143,7 +152,7 @@ describe('autocomplete', () => { describe('show', () => { testSuggestions('show ', ['INFO']); for (const fn of ['info']) { - testSuggestions(`show ${fn} `, ['|']); + testSuggestions(`show ${fn} `, ['| ']); } }); @@ -151,58 +160,51 @@ describe('autocomplete', () => { const allEvalFns = getFunctionSignaturesByReturnType('where', 'any', { scalar: true, }); - testSuggestions('from a | where ', [...getFieldNamesByType('any'), ...allEvalFns]); + testSuggestions('from a | where ', [ + ...getFieldNamesByType('any').map((field) => `${field} `), + ...allEvalFns, + ]); testSuggestions('from a | eval var0 = 1 | where ', [ - ...getFieldNamesByType('any'), + ...getFieldNamesByType('any').map((name) => `${name} `), 'var0', ...allEvalFns, ]); - testSuggestions('from a | where stringField ', [ - // all functions compatible with a stringField type - ...getFunctionSignaturesByReturnType( - 'where', - 'boolean', - { - builtin: true, - }, - ['string'] - ), - ]); - testSuggestions('from a | where textField >= ', [ - ...getFieldNamesByType('any'), - ...getFunctionSignaturesByReturnType('where', ['any'], { scalar: true }), - ]); - // Skip these tests until the insensitive case equality gets restored back - testSuggestions.skip('from a | where stringField =~ ', [ - ...getFieldNamesByType('string'), - ...getFunctionSignaturesByReturnType('where', 'string', { scalar: true }), - ]); - testSuggestions('from a | where textField >= textField', [ - ...getFieldNamesByType('any'), - ...getFunctionSignaturesByReturnType('where', 'any', { scalar: true }), - ]); - testSuggestions.skip('from a | where stringField =~ stringField ', [ - '|', + testSuggestions('from a | where keywordField ', [ + // all functions compatible with a keywordField type ...getFunctionSignaturesByReturnType( 'where', 'boolean', { builtin: true, }, - ['boolean'] + undefined, + ['and', 'or', 'not'] ), ]); + const expectedComparisonWithTextFieldSuggestions = [ + ...getFieldNamesByType(['text', 'keyword', 'ip', 'version']), + ...getFunctionSignaturesByReturnType('where', ['text', 'keyword', 'ip', 'version'], { + scalar: true, + }), + ]; + testSuggestions('from a | where textField >= ', expectedComparisonWithTextFieldSuggestions); + testSuggestions( + 'from a | where textField >= textField', + expectedComparisonWithTextFieldSuggestions + ); for (const op of ['and', 'or']) { - testSuggestions(`from a | where stringField >= stringField ${op} `, [ + testSuggestions(`from a | where keywordField >= keywordField ${op} `, [ ...getFieldNamesByType('any'), ...getFunctionSignaturesByReturnType('where', 'any', { scalar: true }), ]); - testSuggestions(`from a | where stringField >= stringField ${op} doubleField `, [ + testSuggestions(`from a | where keywordField >= keywordField ${op} doubleField `, [ ...getFunctionSignaturesByReturnType('where', 'boolean', { builtin: true }, ['double']), ]); - testSuggestions(`from a | where stringField >= stringField ${op} doubleField == `, [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('where', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + testSuggestions(`from a | where keywordField >= keywordField ${op} doubleField == `, [ + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('where', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); } testSuggestions('from a | stats a=avg(doubleField) | where a ', [ @@ -225,10 +227,10 @@ describe('autocomplete', () => { testSuggestions( 'from a | where log10()', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(log10ParameterTypes), ...getFunctionSignaturesByReturnType( 'where', - ESQL_NUMERIC_TYPES, + log10ParameterTypes, { scalar: true }, undefined, ['log10'] @@ -243,10 +245,10 @@ describe('autocomplete', () => { testSuggestions( 'from a | WHERE pow(doubleField, )', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(powParameterTypes), ...getFunctionSignaturesByReturnType( 'where', - ESQL_NUMERIC_TYPES, + powParameterTypes, { scalar: true }, undefined, ['pow'] @@ -255,8 +257,8 @@ describe('autocomplete', () => { ',' ); - testSuggestions('from index | WHERE stringField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); - testSuggestions('from index | WHERE stringField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | WHERE keywordField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | WHERE keywordField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); testSuggestions('from index | WHERE not ', [ ...getFieldNamesByType('boolean'), ...getFunctionSignaturesByReturnType('eval', 'boolean', { scalar: true }), @@ -299,15 +301,19 @@ describe('autocomplete', () => { const constantPattern = '"%{WORD:firstWord}"'; const subExpressions = [ '', - `grok stringField |`, - `grok stringField ${constantPattern} |`, - `dissect stringField ${constantPattern} append_separator = ":" |`, - `dissect stringField ${constantPattern} |`, + `grok keywordField |`, + `grok keywordField ${constantPattern} |`, + `dissect keywordField ${constantPattern} append_separator = ":" |`, + `dissect keywordField ${constantPattern} |`, ]; for (const subExpression of subExpressions) { - testSuggestions(`from a | ${subExpression} grok `, getFieldNamesByType('string')); - testSuggestions(`from a | ${subExpression} grok stringField `, [constantPattern], ' '); - testSuggestions(`from a | ${subExpression} grok stringField ${constantPattern} `, ['|']); + // Unskip once https://github.com/elastic/kibana/issues/190070 is fixed + testSuggestions.skip( + `from a | ${subExpression} grok `, + getFieldNamesByType(ESQL_STRING_TYPES) + ); + testSuggestions(`from a | ${subExpression} grok keywordField `, [constantPattern], ' '); + testSuggestions(`from a | ${subExpression} grok keywordField ${constantPattern} `, ['| ']); } }); @@ -315,67 +321,71 @@ describe('autocomplete', () => { const constantPattern = '"%{firstWord}"'; const subExpressions = [ '', - `dissect stringField |`, - `dissect stringField ${constantPattern} |`, - `dissect stringField ${constantPattern} append_separator = ":" |`, + `dissect keywordField |`, + `dissect keywordField ${constantPattern} |`, + `dissect keywordField ${constantPattern} append_separator = ":" |`, ]; for (const subExpression of subExpressions) { - testSuggestions(`from a | ${subExpression} dissect `, getFieldNamesByType('string')); - testSuggestions(`from a | ${subExpression} dissect stringField `, [constantPattern], ' '); + // Unskip once https://github.com/elastic/kibana/issues/190070 is fixed + testSuggestions.skip( + `from a | ${subExpression} dissect `, + getFieldNamesByType(ESQL_STRING_TYPES) + ); + testSuggestions(`from a | ${subExpression} dissect keywordField `, [constantPattern], ' '); testSuggestions( - `from a | ${subExpression} dissect stringField ${constantPattern} `, - ['APPEND_SEPARATOR = $0', '|'], + `from a | ${subExpression} dissect keywordField ${constantPattern} `, + ['APPEND_SEPARATOR = $0', '| '], ' ' ); testSuggestions( - `from a | ${subExpression} dissect stringField ${constantPattern} append_separator = `, + `from a | ${subExpression} dissect keywordField ${constantPattern} append_separator = `, ['":"', '";"'] ); testSuggestions( - `from a | ${subExpression} dissect stringField ${constantPattern} append_separator = ":" `, - ['|'] + `from a | ${subExpression} dissect keywordField ${constantPattern} append_separator = ":" `, + ['| '] ); } }); describe('sort', () => { testSuggestions('from a | sort ', [ - ...getFieldNamesByType('any'), + ...getFieldNamesByType('any').map((name) => `${name} `), ...getFunctionSignaturesByReturnType('sort', 'any', { scalar: true }), ]); - testSuggestions('from a | sort stringField ', ['ASC', 'DESC', ',', '|']); - testSuggestions('from a | sort stringField desc ', ['NULLS FIRST', 'NULLS LAST', ',', '|']); + testSuggestions('from a | sort keywordField ', ['ASC ', 'DESC ', ',', '| ']); + testSuggestions('from a | sort keywordField desc ', ['NULLS FIRST ', 'NULLS LAST ', ',', '| ']); // @TODO: improve here - // testSuggestions('from a | sort stringField desc ', ['first', 'last']); + // testSuggestions('from a | sort keywordField desc ', ['first', 'last']); }); describe('limit', () => { - testSuggestions('from a | limit ', ['10', '100', '1000']); - testSuggestions('from a | limit 4 ', ['|']); + testSuggestions('from a | limit ', ['10 ', '100 ', '1000 ']); + testSuggestions('from a | limit 4 ', ['| ']); }); describe('mv_expand', () => { testSuggestions('from a | mv_expand ', getFieldNamesByType('any')); - testSuggestions('from a | mv_expand a ', ['|']); + testSuggestions('from a | mv_expand a ', ['| ']); }); describe('rename', () => { testSuggestions('from a | rename ', getFieldNamesByType('any')); - testSuggestions('from a | rename stringField ', ['AS $0'], ' '); - testSuggestions('from a | rename stringField as ', ['var0']); + testSuggestions('from a | rename keywordField ', ['AS $0'], ' '); + testSuggestions('from a | rename keywordField as ', ['var0']); }); for (const command of ['keep', 'drop']) { describe(command, () => { testSuggestions(`from a | ${command} `, getFieldNamesByType('any')); testSuggestions( - `from a | ${command} stringField, `, - getFieldNamesByType('any').filter((name) => name !== 'stringField') + `from a | ${command} keywordField, `, + getFieldNamesByType('any').filter((name) => name !== 'keywordField') ); testSuggestions( - `from a | ${command} stringField,`, - getFieldNamesByType('any').filter((name) => name !== 'stringField'), + `from a | ${command} keywordField,`, + getFieldNamesByType('any').filter((name) => name !== 'keywordField'), ',' ); @@ -413,59 +423,51 @@ describe('autocomplete', () => { testSuggestions(`from a ${prevCommand}| enrich _${mode.toUpperCase()}:`, policyNames, ':'); testSuggestions(`from a ${prevCommand}| enrich _${camelCase(mode)}:`, policyNames, ':'); } - testSuggestions(`from a ${prevCommand}| enrich policy `, ['ON $0', 'WITH $0', '|']); - testSuggestions(`from a ${prevCommand}| enrich policy on `, [ - 'stringField', - 'doubleField', - 'dateField', - 'booleanField', - 'ipField', - 'geoPointField', - 'geoShapeField', - 'cartesianPointField', - 'cartesianShapeField', - '`any#Char$Field`', - 'kubernetes.something.something', - ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b `, ['WITH $0', ',', '|']); + testSuggestions(`from a ${prevCommand}| enrich policy `, ['ON $0', 'WITH $0', '| ']); + testSuggestions(`from a ${prevCommand}| enrich policy on `, getFieldNamesByType('any')); + testSuggestions(`from a ${prevCommand}| enrich policy on b `, ['WITH $0', ',', '| ']); testSuggestions( `from a ${prevCommand}| enrich policy on b with `, - ['var0 =', ...getPolicyFields('policy')], + ['var0 = ', ...getPolicyFields('policy')], ' ' ); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 `, ['= $0', ',', '|']); + 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 = keywordField `, [ ',', - '|', + '| ', ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField, `, [ - 'var1 =', + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = keywordField, `, [ + 'var1 = ', ...getPolicyFields('policy'), ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField, var1 `, [ + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = keywordField, var1 `, [ '= $0', ',', - '|', + '| ', ]); testSuggestions( - `from a ${prevCommand}| enrich policy on b with var0 = stringField, var1 = `, + `from a ${prevCommand}| enrich policy on b with var0 = keywordField, var1 = `, [...getPolicyFields('policy')] ); testSuggestions( `from a ${prevCommand}| enrich policy with `, - ['var0 =', ...getPolicyFields('policy')], + ['var0 = ', ...getPolicyFields('policy')], ' ' ); - testSuggestions(`from a ${prevCommand}| enrich policy with stringField `, ['= $0', ',', '|']); + testSuggestions(`from a ${prevCommand}| enrich policy with keywordField `, [ + '= $0', + ',', + '| ', + ]); } }); describe('eval', () => { testSuggestions('from a | eval ', [ - 'var0 =', + 'var0 = ', ...getFieldNamesByType('any'), ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ]); @@ -474,10 +476,10 @@ describe('autocomplete', () => { 'double', ]), ',', - '|', + '| ', ]); - testSuggestions('from index | EVAL stringField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); - testSuggestions('from index | EVAL stringField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | EVAL keywordField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | EVAL keywordField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); testSuggestions('from index | EVAL doubleField in ', ['( $0 )']); testSuggestions( 'from index | EVAL doubleField in ( )', @@ -499,23 +501,23 @@ describe('autocomplete', () => { ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ]); testSuggestions('from a | eval a=doubleField, ', [ - 'var0 =', + 'var0 = ', ...getFieldNamesByType('any'), 'a', ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ]); // Skip this test until the insensitive case equality gets restored back - testSuggestions.skip('from a | eval a=stringField =~ ', [ - ...getFieldNamesByType('string'), - ...getFunctionSignaturesByReturnType('eval', 'string', { scalar: true }), + testSuggestions.skip('from a | eval a=keywordField =~ ', [ + ...getFieldNamesByType(ESQL_STRING_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_STRING_TYPES, { scalar: true }), ]); testSuggestions( 'from a | eval a=round()', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] @@ -548,7 +550,7 @@ describe('autocomplete', () => { ); testSuggestions('from a | eval a=round(doubleField) ', [ ',', - '|', + '| ', ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ 'double', ]), @@ -566,6 +568,7 @@ describe('autocomplete', () => { testSuggestions( 'from a | eval round(doubleField, ', [ + ...getFieldNamesByType('integer'), ...getFunctionSignaturesByReturnType('eval', 'integer', { scalar: true }, undefined, [ 'round', ]), @@ -573,31 +576,39 @@ describe('autocomplete', () => { ' ' ); testSuggestions('from a | eval a=round(doubleField),', [ - 'var0 =', + 'var0 = ', ...getFieldNamesByType('any'), 'a', ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ]); testSuggestions('from a | eval a=round(doubleField) + ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions('from a | eval a=round(doubleField)+ ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions('from a | eval a=doubleField+ ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions('from a | eval a=`any#Char$Field`+ ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions( - 'from a | stats avg(doubleField) by stringField | eval ', + 'from a | stats avg(doubleField) by keywordField | eval ', [ - 'var0 =', + 'var0 = ', '`avg(doubleField)`', ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ], @@ -609,7 +620,7 @@ describe('autocomplete', () => { testSuggestions( 'from a | eval abs(doubleField) + 1 | eval ', [ - 'var0 =', + 'var0 = ', ...getFieldNamesByType('any'), '`abs(doubleField) + 1`', ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), @@ -617,9 +628,9 @@ describe('autocomplete', () => { ' ' ); testSuggestions( - 'from a | stats avg(doubleField) by stringField | eval ', + 'from a | stats avg(doubleField) by keywordField | eval ', [ - 'var0 =', + 'var0 = ', '`avg(doubleField)`', ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ], @@ -629,9 +640,9 @@ describe('autocomplete', () => { [[{ name: 'avg_doubleField_', type: 'double' }], undefined, undefined] ); testSuggestions( - 'from a | stats avg(doubleField), avg(kubernetes.something.something) by stringField | eval ', + 'from a | stats avg(doubleField), avg(kubernetes.something.something) by keywordField | eval ', [ - 'var0 =', + 'var0 = ', '`avg(doubleField)`', '`avg(kubernetes.something.something)`', ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), @@ -648,13 +659,14 @@ describe('autocomplete', () => { undefined, ] ); + testSuggestions( 'from a | eval a=round(doubleField), b=round()', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] @@ -664,7 +676,7 @@ describe('autocomplete', () => { ); // test that comma is correctly added to the suggestions if minParams is not reached yet testSuggestions('from a | eval a=concat( ', [ - ...getFieldNamesByType(['text', 'keyword']).map((v) => `${v},`), + ...getFieldNamesByType(['text', 'keyword']).map((v) => `${v}, `), ...getFunctionSignaturesByReturnType( 'eval', ['text', 'keyword'], @@ -692,19 +704,15 @@ describe('autocomplete', () => { 'from a | eval a=cidr_match(ipField, textField, ', [ ...getFieldNamesByType('text'), - ...getFunctionSignaturesByReturnType( - 'eval', - ['text', 'keyword'], - { scalar: true }, - undefined, - ['cidr_match'] - ), + ...getFunctionSignaturesByReturnType('eval', 'text', { scalar: true }, undefined, [ + 'cidr_match', + ]), ], ' ' ); // test that comma is correctly added to the suggestions if minParams is not reached yet testSuggestions('from a | eval a=cidr_match( ', [ - ...getFieldNamesByType('ip').map((v) => `${v},`), + ...getFieldNamesByType('ip').map((v) => `${v}, `), ...getFunctionSignaturesByReturnType('eval', 'ip', { scalar: true }, undefined, [ 'cidr_match', ]).map((v) => ({ ...v, text: `${v.text},` })), @@ -727,14 +735,15 @@ describe('autocomplete', () => { // 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(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] @@ -744,12 +753,14 @@ describe('autocomplete', () => { ); } + const absParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; + // Smoke testing for suggestions in previous position than the end of the statement testSuggestions( 'from a | eval var0 = abs(doubleField) | eval abs(var0)', [ ',', - '|', + '| ', ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ 'double', ]), @@ -760,10 +771,10 @@ describe('autocomplete', () => { testSuggestions( 'from a | eval var0 = abs(b) | eval abs(var0)', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(absParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + absParameterTypes, { scalar: true }, undefined, ['abs'] @@ -776,7 +787,7 @@ describe('autocomplete', () => { // Test suggestions for each possible param, within each signature variation, for each function for (const fn of evalFunctionDefinitions) { // skip this fn for the moment as it's quite hard to test - if (!['bucket', 'date_extract', 'date_diff'].includes(fn.name)) { + if (!['bucket', 'date_extract', 'date_diff', 'case'].includes(fn.name)) { for (const signature of fn.signatures) { signature.params.forEach((param, i) => { if (i < signature.params.length) { @@ -794,11 +805,11 @@ describe('autocomplete', () => { // get all possible types for this param const [constantOnlyParamDefs, acceptsFieldParamDefs] = partition( allParamDefs, - (p) => p.constantOnly || /_literal/.test(p.type) + (p) => p.constantOnly || /_literal/.test(p.type as string) ); - const getTypesFromParamDefs = (paramDefs: FunctionParameter[]) => - Array.from(new Set(paramDefs.map((p) => p.type))); + const getTypesFromParamDefs = (paramDefs: FunctionParameter[]): SupportedDataType[] => + Array.from(new Set(paramDefs.map((p) => p.type))).filter(isSupportedDataType); const suggestedConstants = param.literalSuggestions || param.literalOptions; @@ -807,16 +818,20 @@ describe('autocomplete', () => { if (!requiresMoreArgs || s === '' || (typeof s === 'object' && s.text === '')) { return s; } - return typeof s === 'string' ? `${s},` : { ...s, text: `${s.text},` }; + return typeof s === 'string' ? `${s}, ` : { ...s, text: `${s.text},` }; }; testSuggestions( `from a | eval ${fn.name}(${Array(i).fill('field').join(', ')}${i ? ',' : ''} )`, suggestedConstants?.length - ? suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ',' : ''}`) + ? suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ', ' : ''}`) : [ - ...getDateLiteralsByFieldType(getTypesFromParamDefs(acceptsFieldParamDefs)), - ...getFieldNamesByType(getTypesFromParamDefs(acceptsFieldParamDefs)), + ...getDateLiteralsByFieldType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), + ...getFieldNamesByType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), ...getFunctionSignaturesByReturnType( 'eval', getTypesFromParamDefs(acceptsFieldParamDefs), @@ -833,10 +848,14 @@ describe('autocomplete', () => { i ? ',' : '' } )`, suggestedConstants?.length - ? suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ',' : ''}`) + ? suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ', ' : ''}`) : [ - ...getDateLiteralsByFieldType(getTypesFromParamDefs(acceptsFieldParamDefs)), - ...getFieldNamesByType(getTypesFromParamDefs(acceptsFieldParamDefs)), + ...getDateLiteralsByFieldType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), + ...getFieldNamesByType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), ...getFunctionSignaturesByReturnType( 'eval', getTypesFromParamDefs(acceptsFieldParamDefs), @@ -865,7 +884,7 @@ describe('autocomplete', () => { testSuggestions( `from a | eval ${fn.name}(`, suggestedConstants?.length - ? [...suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ',' : ''}`)] + ? [...suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ', ' : ''}`)] : [] ); } @@ -881,7 +900,7 @@ describe('autocomplete', () => { [ ...dateSuggestions, ',', - '|', + '| ', ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ 'integer', ]), @@ -890,12 +909,12 @@ describe('autocomplete', () => { ); testSuggestions('from a | eval a = 1 year ', [ ',', - '|', + '| ', ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true, skipAssign: true }, [ 'time_interval', ]), ]); - testSuggestions('from a | eval a = 1 day + 2 ', [',', '|']); + testSuggestions('from a | eval a = 1 day + 2 ', [',', '| ']); testSuggestions( 'from a | eval 1 day + 2 ', [ @@ -908,19 +927,19 @@ describe('autocomplete', () => { ); testSuggestions( 'from a | eval var0=date_trunc()', - [...getLiteralsByType('time_literal').map((t) => `${t},`)], + getLiteralsByType('time_literal').map((t) => `${t}, `), '(' ); testSuggestions( 'from a | eval var0=date_trunc(2 )', - [...dateSuggestions.map((t) => `${t},`), ','], + [...dateSuggestions.map((t) => `${t}, `), ','], ' ' ); }); }); describe('values suggestions', () => { - testSuggestions('FROM "a"', ['a', 'b'], undefined, 7, [ + testSuggestions('FROM "a"', ['a ', 'b '], undefined, 7, [ , [ { name: 'a', hidden: false }, @@ -945,7 +964,7 @@ describe('autocomplete', () => { 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(doubleField) '; + const statement = 'from a | drop keywordField | eval var0 = abs(doubleField) '; const triggerOffset = statement.lastIndexOf(' '); const context = createCompletionContext(statement[triggerOffset]); await suggest( @@ -956,7 +975,7 @@ describe('autocomplete', () => { callbackMocks ); expect(callbackMocks.getFieldsFor).toHaveBeenCalledWith({ - query: 'from a | drop stringField', + query: 'from a | drop keywordField', }); }); it('should send the fields query aware of the location', async () => { @@ -975,50 +994,6 @@ describe('autocomplete', () => { }); }); - describe('auto triggers', () => { - function getSuggestionsFor(statement: string) { - const callbackMocks = createCustomCallbackMocks(undefined, undefined, undefined); - const triggerOffset = statement.lastIndexOf(' ') + 1; // drop <here> - const context = createCompletionContext(statement[triggerOffset]); - return suggest( - statement, - triggerOffset + 1, - context, - async (text) => (text ? getAstAndSyntaxErrors(text) : { ast: [], errors: [] }), - callbackMocks - ); - } - it('should trigger further suggestions for functions', async () => { - const suggestions = await getSuggestionsFor('from a | eval '); - // test that all functions will retrigger suggestions - expect( - suggestions - .filter(({ kind }) => kind === 'Function') - .every(({ command }) => command === TRIGGER_SUGGESTION_COMMAND) - ).toBeTruthy(); - // now test that non-function won't retrigger - expect( - suggestions - .filter(({ kind }) => kind !== 'Function') - .every(({ command }) => command == null) - ).toBeTruthy(); - }); - it('should trigger further suggestions for commands', async () => { - const suggestions = await getSuggestionsFor('from a | '); - // test that all commands will retrigger suggestions - expect( - suggestions.every(({ command }) => command === TRIGGER_SUGGESTION_COMMAND) - ).toBeTruthy(); - }); - it('should trigger further suggestions after enrich mode', async () => { - const suggestions = await getSuggestionsFor('from a | enrich _any:'); - // test that all commands will retrigger suggestions - expect( - suggestions.every(({ command }) => command === TRIGGER_SUGGESTION_COMMAND) - ).toBeTruthy(); - }); - }); - /** * Monaco asks for suggestions in at least two different scenarios. * 1. When the user types a non-whitespace character (e.g. 'FROM k') - this is the Invoke trigger kind @@ -1049,24 +1024,47 @@ describe('autocomplete', () => { 10 ); - // function argument - testSuggestions( - 'FROM kibana_sample_data_logs | EVAL TRIM(e)', - [ - ...getFunctionSignaturesByReturnType( - 'eval', - ['text', 'keyword'], - { scalar: true }, - undefined, - ['trim'] - ), - ], - undefined, - 42 - ); + describe('function arguments', () => { + // function argument + testSuggestions( + 'FROM kibana_sample_data_logs | EVAL TRIM(e)', + [ + ...getFieldNamesByType(['text', 'keyword']), + ...getFunctionSignaturesByReturnType( + 'eval', + ['text', 'keyword'], + { scalar: true }, + undefined, + ['trim'] + ), + ], + undefined, + 42 + ); + + // subsequent function argument + const expectedDateDiff2ndArgSuggestions = [ + TIME_PICKER_SUGGESTION, + ...TIME_SYSTEM_PARAMS.map((t) => `${t}, `), + ...getFieldNamesByType('date').map((name) => `${name}, `), + ...getFunctionSignaturesByReturnType('eval', 'date', { scalar: true }).map((s) => ({ + ...s, + text: `${s.text},`, + })), + ]; + testSuggestions( + 'FROM a | EVAL DATE_DIFF("day", )', + expectedDateDiff2ndArgSuggestions, + undefined, + 31 + ); + + // trigger character case for comparison + testSuggestions('FROM a | EVAL DATE_DIFF("day", )', expectedDateDiff2ndArgSuggestions, ' '); + }); // FROM source - testSuggestions('FROM k', ['index1', 'index2'], undefined, 6, [ + testSuggestions('FROM k', ['index1 ', 'index2 '], undefined, 6, [ , [ { name: 'index1', hidden: false }, @@ -1075,7 +1073,7 @@ describe('autocomplete', () => { ]); // FROM source METADATA - testSuggestions('FROM index1 M', [',', 'METADATA $0', '|'], undefined, 13); + testSuggestions('FROM index1 M', [',', 'METADATA $0', '| '], undefined, 13); // FROM source METADATA field testSuggestions('FROM index1 METADATA _', METADATA_FIELDS, undefined, 22); @@ -1084,7 +1082,7 @@ describe('autocomplete', () => { testSuggestions( 'FROM index1 | EVAL b', [ - 'var0 =', + 'var0 = ', ...getFieldNamesByType('any'), ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ], @@ -1100,7 +1098,13 @@ describe('autocomplete', () => { ); // DISSECT field - testSuggestions('FROM index1 | DISSECT b', getFieldNamesByType('string'), undefined, 23); + // enable once https://github.com/elastic/kibana/issues/190070 is fixed + testSuggestions.skip( + 'FROM index1 | DISSECT b', + getFieldNamesByType(ESQL_STRING_TYPES), + undefined, + 23 + ); // DROP (first field) testSuggestions('FROM index1 | DROP f', getFieldNamesByType('any'), undefined, 20); @@ -1117,7 +1121,7 @@ describe('autocomplete', () => { ); // ENRICH policy ON - testSuggestions('FROM index1 | ENRICH policy O', ['ON $0', 'WITH $0', '|'], undefined, 29); + testSuggestions('FROM index1 | ENRICH policy O', ['ON $0', 'WITH $0', '| '], undefined, 29); // ENRICH policy ON field testSuggestions('FROM index1 | ENRICH policy ON f', getFieldNamesByType('any'), undefined, 32); @@ -1125,20 +1129,26 @@ describe('autocomplete', () => { // ENRICH policy WITH policyfield testSuggestions( 'FROM index1 | ENRICH policy WITH v', - ['var0 =', ...getPolicyFields('policy')], + ['var0 = ', ...getPolicyFields('policy')], undefined, 34 ); testSuggestions( 'FROM index1 | ENRICH policy WITH \tv', - ['var0 =', ...getPolicyFields('policy')], + ['var0 = ', ...getPolicyFields('policy')], undefined, 34 ); // GROK field - testSuggestions('FROM index1 | GROK f', getFieldNamesByType('string'), undefined, 20); + // enable once https://github.com/elastic/kibana/issues/190070 + testSuggestions.skip( + 'FROM index1 | GROK f', + getFieldNamesByType(ESQL_STRING_TYPES), + undefined, + 20 + ); // KEEP (first field) testSuggestions('FROM index1 | KEEP f', getFieldNamesByType('any'), undefined, 20); @@ -1154,7 +1164,7 @@ describe('autocomplete', () => { // LIMIT argument // Here we actually test that the invoke trigger kind does not work // because it isn't very useful to see literal suggestions when typing a number - testSuggestions('FROM a | LIMIT 1', ['|'], undefined, 16); + testSuggestions('FROM a | LIMIT 1', ['| '], undefined, 16); // MV_EXPAND field testSuggestions('FROM index1 | MV_EXPAND f', getFieldNamesByType('any'), undefined, 25); @@ -1173,41 +1183,49 @@ describe('autocomplete', () => { 'FROM index1 | SORT f', [ ...getFunctionSignaturesByReturnType('sort', 'any', { scalar: true }), - ...getFieldNamesByType('any'), + ...getFieldNamesByType('any').map((field) => `${field} `), ], undefined, 20 ); // SORT field order - testSuggestions('FROM index1 | SORT stringField a', ['ASC', 'DESC', ',', '|'], undefined, 32); + testSuggestions( + 'FROM index1 | SORT keywordField a', + ['ASC ', 'DESC ', ',', '| '], + undefined, + 33 + ); // SORT field order nulls testSuggestions( - 'FROM index1 | SORT stringField ASC n', - ['NULLS FIRST', 'NULLS LAST', ',', '|'], + 'FROM index1 | SORT keywordField ASC n', + ['NULLS FIRST ', 'NULLS LAST ', ',', '| '], undefined, - 36 + 37 ); // STATS argument testSuggestions( 'FROM index1 | STATS f', - ['var0 =', ...getFunctionSignaturesByReturnType('stats', 'any', { scalar: true, agg: true })], + [ + 'var0 = ', + ...getFunctionSignaturesByReturnType('stats', 'any', { scalar: true, agg: true }), + ], undefined, 21 ); // STATS argument BY - testSuggestions('FROM index1 | STATS AVG(booleanField) B', ['BY $0', ',', '|'], undefined, 39); + testSuggestions('FROM index1 | STATS AVG(booleanField) B', ['BY $0', ',', '| '], undefined, 39); // STATS argument BY expression testSuggestions( 'FROM index1 | STATS field BY f', [ - 'var0 =', + 'var0 = ', ...getFunctionSignaturesByReturnType('stats', 'any', { grouping: true, scalar: true }), - ...getFieldNamesByType('any'), + ...getFieldNamesByType('any').map((field) => `${field} `), ], undefined, 30 @@ -1217,7 +1235,7 @@ describe('autocomplete', () => { testSuggestions( 'FROM index1 | WHERE f', [ - ...getFieldNamesByType('any'), + ...getFieldNamesByType('any').map((field) => `${field} `), ...getFunctionSignaturesByReturnType('where', 'any', { scalar: true }), ], undefined, @@ -1226,17 +1244,289 @@ describe('autocomplete', () => { // WHERE argument comparison testSuggestions( - 'FROM index1 | WHERE stringField i', + 'FROM index1 | WHERE keywordField i', getFunctionSignaturesByReturnType( 'where', 'boolean', { builtin: true, }, - ['string'] + undefined, + ['and', 'or', 'not'] ), undefined, - 33 + 34 + ); + }); + + describe('advancing the cursor and opening the suggestion menu automatically ✨', () => { + const attachTriggerCommand = ( + s: string | PartialSuggestionWithText + ): PartialSuggestionWithText => + typeof s === 'string' + ? { + text: s, + command: TRIGGER_SUGGESTION_COMMAND, + } + : { ...s, command: TRIGGER_SUGGESTION_COMMAND }; + + const attachAsSnippet = (s: PartialSuggestionWithText): PartialSuggestionWithText => ({ + ...s, + asSnippet: true, + }); + + // Source command + testSuggestions( + 'F', + ['FROM $0', 'ROW $0', 'SHOW $0', 'METRICS $0'].map(attachTriggerCommand).map(attachAsSnippet), + undefined, + 1 + ); + + // Pipe command + testSuggestions( + 'FROM a | E', + commandDefinitions + .filter(({ name }) => !sourceCommands.includes(name)) + .map(({ name }) => attachTriggerCommand(name.toUpperCase() + ' $0')) + .map(attachAsSnippet), // TODO consider making this check more fundamental + undefined, + 10 + ); + + describe('function arguments', () => { + // literalSuggestions parameter + const dateDiffFirstParamSuggestions = + evalFunctionDefinitions.find(({ name }) => name === 'date_diff')?.signatures[0].params?.[0] + .literalSuggestions ?? []; + testSuggestions( + 'FROM a | EVAL DATE_DIFF()', + dateDiffFirstParamSuggestions.map((s) => `"${s}", `).map(attachTriggerCommand), + undefined, + 24 + ); + + // field parameter + + const expectedStringSuggestionsWhenMoreArgsAreNeeded = [ + ...getFieldNamesByType(ESQL_STRING_TYPES) + .map((field) => `${field}, `) + .map(attachTriggerCommand), + ...getFunctionSignaturesByReturnType( + 'eval', + ESQL_STRING_TYPES, + { scalar: true }, + undefined, + ['replace'] + ).map((s) => ({ + ...s, + text: `${s.text},`, + })), + ]; + + testSuggestions( + 'FROM a | EVAL REPLACE()', + expectedStringSuggestionsWhenMoreArgsAreNeeded, + undefined, + 22 + ); + + // subsequent parameter + testSuggestions( + 'FROM a | EVAL REPLACE(keywordField, )', + expectedStringSuggestionsWhenMoreArgsAreNeeded, + undefined, + 36 + ); + + // final parameter — should not advance! + testSuggestions( + 'FROM a | EVAL REPLACE(keywordField, keywordField, )', + [ + ...getFieldNamesByType(ESQL_STRING_TYPES).map((field) => ({ + text: field, + command: undefined, + })), + ...getFunctionSignaturesByReturnType( + 'eval', + ESQL_STRING_TYPES, + { scalar: true }, + undefined, + ['replace'] + ), + ], + undefined, + 50 + ); + + // Trigger character because this is how it will actually be... the user will press + // space-bar... this may change if we fix the tokenization of timespan literals + // such that "2 days" is a single monaco token + testSuggestions( + 'FROM a | EVAL DATE_TRUNC(2 )', + [...timeUnitsToSuggest.map((s) => `${s.name}, `).map(attachTriggerCommand), ','], + ' ' + ); + }); + + // PIPE (|) + testSuggestions( + 'FROM a ', + [attachTriggerCommand('| '), ',', attachAsSnippet(attachTriggerCommand('METADATA $0'))], + undefined, + 7 + ); + + // Assignment + testSuggestions(`FROM a | ENRICH policy on b with `, [ + attachTriggerCommand('var0 = '), + ...getPolicyFields('policy'), + ]); + + // FROM source + // + // Using an Invoke trigger kind here because that's what Monaco uses when the show suggestions + // action is triggered (e.g. accepting the "FROM" suggestion) + testSuggestions( + 'FROM ', + [ + { text: 'index1 ', command: TRIGGER_SUGGESTION_COMMAND }, + { text: 'index2 ', command: TRIGGER_SUGGESTION_COMMAND }, + ], + undefined, + 5, + [ + , + [ + { name: 'index1', hidden: false }, + { name: 'index2', hidden: false }, + ], + ] + ); + + // FROM source METADATA + testSuggestions( + 'FROM index1 M', + [',', attachAsSnippet(attachTriggerCommand('METADATA $0')), '| '], + undefined, + 13 + ); + + // LIMIT number + testSuggestions('FROM a | LIMIT ', ['10 ', '100 ', '1000 '].map(attachTriggerCommand)); + + // SORT field + testSuggestions( + 'FROM a | SORT ', + [ + ...getFieldNamesByType('any').map((field) => `${field} `), + ...getFunctionSignaturesByReturnType('sort', 'any', { scalar: true }), + ].map(attachTriggerCommand), + undefined, + 14 + ); + + // SORT field order + testSuggestions( + 'FROM a | SORT field ', + [',', ...['ASC ', 'DESC ', '| '].map(attachTriggerCommand)], + undefined, + 20 + ); + + // SORT field order nulls + testSuggestions( + 'FROM a | SORT field ASC ', + [',', ...['NULLS FIRST ', 'NULLS LAST ', '| '].map(attachTriggerCommand)], + undefined, + 24 + ); + + // STATS argument + testSuggestions( + 'FROM a | STATS ', + [ + 'var0 = ', + ...getFunctionSignaturesByReturnType('stats', 'any', { scalar: true, agg: true }).map( + attachAsSnippet + ), + ].map(attachTriggerCommand), + undefined, + 15 + ); + + // STATS argument BY + testSuggestions( + 'FROM a | STATS AVG(numberField) ', + [',', attachAsSnippet(attachTriggerCommand('BY $0')), attachTriggerCommand('| ')], + undefined, + 32 + ); + + // STATS argument BY field + const allByCompatibleFunctions = getFunctionSignaturesByReturnType( + 'stats', + 'any', + { + scalar: true, + grouping: true, + }, + undefined, + undefined, + 'by' + ); + testSuggestions( + 'FROM a | STATS AVG(numberField) BY ', + [ + attachTriggerCommand('var0 = '), + ...getFieldNamesByType('any') + .map((field) => `${field} `) + .map(attachTriggerCommand), + ...allByCompatibleFunctions, + ], + undefined, + 35 + ); + + // STATS argument BY assignment (checking field suggestions) + testSuggestions( + 'FROM a | STATS AVG(numberField) BY var0 = ', + [ + ...getFieldNamesByType('any') + .map((field) => `${field} `) + .map(attachTriggerCommand), + ...allByCompatibleFunctions, + ], + undefined, + 41 + ); + + // WHERE argument (field suggestions) + testSuggestions( + 'FROM a | WHERE ', + [ + ...getFieldNamesByType('any') + .map((field) => `${field} `) + .map(attachTriggerCommand), + ...getFunctionSignaturesByReturnType('where', 'any', { scalar: true }).map(attachAsSnippet), + ], + undefined, + 15 + ); + + // WHERE argument comparison + testSuggestions( + 'FROM a | WHERE keywordField ', + getFunctionSignaturesByReturnType( + 'where', + 'boolean', + { + builtin: true, + }, + ['keyword'] + ).map((s) => (s.text.toLowerCase().includes('null') ? s : attachTriggerCommand(s))), + undefined, + 28 ); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts index d2d617aac4315..e775c3f05fe5f 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts @@ -13,10 +13,11 @@ import type { ESQLCommand, ESQLCommandOption, ESQLFunction, + ESQLLiteral, ESQLSingleAstItem, } from '@kbn/esql-ast'; import { partition } from 'lodash'; -import { ESQL_NUMBER_TYPES, isNumericType } from '../shared/esql_types'; +import { ESQL_NUMBER_TYPES, compareTypesWithLiterals, isNumericType } from '../shared/esql_types'; import type { EditorContext, SuggestionRawDefinition } from './types'; import { lookupColumn, @@ -44,6 +45,7 @@ import { nonNullable, getColumnExists, findPreviousWord, + noCaseCompare, } from '../shared/helpers'; import { collectVariables, excludeVariablesFromCurrentCommand } from '../shared/variables'; import type { ESQLPolicy, ESQLRealField, ESQLVariable, ReferenceMaps } from '../validation/types'; @@ -93,7 +95,7 @@ import { isAggFunctionUsedAlready, removeQuoteForSuggestedSources, } from './helper'; -import { FunctionParameter } from '../definitions/types'; +import { FunctionParameter, FunctionReturnType, SupportedDataType } from '../definitions/types'; type GetSourceFn = () => Promise<SuggestionRawDefinition[]>; type GetDataStreamsForIntegrationFn = ( @@ -101,7 +103,8 @@ type GetDataStreamsForIntegrationFn = ( ) => Promise<Array<{ name: string; title?: string }> | undefined>; type GetFieldsByTypeFn = ( type: string | string[], - ignored?: string[] + ignored?: string[], + options?: { advanceCursorAndOpenSuggestions?: boolean; addComma?: boolean } ) => Promise<SuggestionRawDefinition[]>; type GetFieldsMapFn = () => Promise<Map<string, ESQLRealField>>; type GetPoliciesFn = () => Promise<SuggestionRawDefinition[]>; @@ -185,8 +188,8 @@ function correctQuerySyntax(_query: string, context: EditorContext) { (context.triggerCharacter && charThatNeedMarkers.includes(context.triggerCharacter)) || // monaco.editor.CompletionTriggerKind['Invoke'] === 0 (context.triggerKind === 0 && unclosedRoundBrackets === 0) || - (context.triggerCharacter === ' ' && - (isMathFunction(query, query.length) || isComma(query.trimEnd()[query.trimEnd().length - 1]))) + (context.triggerCharacter === ' ' && isMathFunction(query, query.length)) || + isComma(query.trimEnd()[query.trimEnd().length - 1]) ) { query += EDITOR_MARKER; } @@ -243,6 +246,7 @@ export async function suggest( if (!ast.length) { return suggestions.filter(isSourceCommand); } + return suggestions.filter((def) => !isSourceCommand(def)); } @@ -308,12 +312,19 @@ export async function suggest( return []; } -function getFieldsByTypeRetriever(queryString: string, resourceRetriever?: ESQLCallbacks) { +function getFieldsByTypeRetriever( + queryString: string, + resourceRetriever?: ESQLCallbacks +): { getFieldsByType: GetFieldsByTypeFn; getFieldsMap: GetFieldsMapFn } { const helpers = getFieldsByTypeHelper(queryString, resourceRetriever); return { - getFieldsByType: async (expectedType: string | string[] = 'any', ignored: string[] = []) => { + getFieldsByType: async ( + expectedType: string | string[] = 'any', + ignored: string[] = [], + options + ) => { const fields = await helpers.getFieldsByType(expectedType, ignored); - return buildFieldsDefinitionsWithMetadata(fields); + return buildFieldsDefinitionsWithMetadata(fields, options); }, getFieldsMap: helpers.getFieldsMap, }; @@ -429,7 +440,13 @@ function areCurrentArgsValid( function extractFinalTypeFromArg( arg: ESQLAstItem, references: Pick<ReferenceMaps, 'fields' | 'variables'> -): string | undefined { +): + | ESQLLiteral['literalType'] + | SupportedDataType + | FunctionReturnType + | 'timeInterval' + | string // @TODO remove this + | undefined { if (Array.isArray(arg)) { return extractFinalTypeFromArg(arg[0], references); } @@ -771,7 +788,7 @@ async function getExpressionSuggestionsByType( option, argDef, nodeArg, - nodeArgType || 'any', + (nodeArgType as string) || 'any', references, getFieldsByType )) @@ -792,7 +809,11 @@ async function getExpressionSuggestionsByType( // if the definition includes a list of constants, suggest them if (argDef.values) { // ... | <COMMAND> ... <suggest enums> - suggestions.push(...buildConstantsDefinitions(argDef.values)); + suggestions.push( + ...buildConstantsDefinitions(argDef.values, undefined, undefined, { + advanceCursorAndOpenSuggestions: true, + }) + ); } // If the type is specified try to dig deeper in the definition to suggest the best candidate if ( @@ -815,6 +836,7 @@ async function getExpressionSuggestionsByType( // ... | <COMMAND> <suggest> // In this case start suggesting something not strictly based on type suggestions.push( + ...(await getFieldsByType('any', [], { advanceCursorAndOpenSuggestions: true })), ...(await getFieldsOrFunctionsSuggestions( ['any'], command.name, @@ -822,7 +844,7 @@ async function getExpressionSuggestionsByType( getFieldsByType, { functions: true, - fields: true, + fields: false, variables: anyVariables, } )) @@ -861,7 +883,7 @@ async function getExpressionSuggestionsByType( option, argDef, nodeArg, - nodeArgType, + nodeArgType as string, references, getFieldsByType )) @@ -1003,11 +1025,15 @@ async function getBuiltinFunctionNextArgument( if (isFnComplete.reason === 'fewArgs') { const fnDef = getFunctionDefinition(nodeArg.name); - if (fnDef?.signatures.every(({ params }) => params.some(({ type }) => isArrayType(type)))) { + if ( + fnDef?.signatures.every(({ params }) => + params.some(({ type }) => isArrayType(type as string)) + ) + ) { suggestions.push(listCompleteItem); } else { const finalType = nestedType || nodeArgType || 'any'; - const supportedTypes = getSupportedTypesForBinaryOperators(fnDef, finalType); + const supportedTypes = getSupportedTypesForBinaryOperators(fnDef, finalType as string); suggestions.push( ...(await getFieldsOrFunctionsSuggestions( // this is a special case with AND/OR @@ -1016,7 +1042,7 @@ async function getBuiltinFunctionNextArgument( // to actually suggest a wider set of fields/functions finalType === 'boolean' && getFunctionDefinition(nodeArg.name)?.type === 'builtin' ? ['any'] - : supportedTypes, + : (supportedTypes as string[]), command.name, option?.name, getFieldsByType, @@ -1087,7 +1113,11 @@ async function getFieldsOrFunctionsSuggestions( } = {} ): Promise<SuggestionRawDefinition[]> { const filteredFieldsByType = pushItUpInTheList( - (await (fields ? getFieldsByType(types, ignoreFields) : [])) as SuggestionRawDefinition[], + (await (fields + ? getFieldsByType(types, ignoreFields, { + advanceCursorAndOpenSuggestions: commandName === 'sort', + }) + : [])) as SuggestionRawDefinition[], functions ); @@ -1121,11 +1151,7 @@ async function getFieldsOrFunctionsSuggestions( } } - // could also be in stats (bucket) but our autocomplete is not great yet - const displayDateSuggestions = types.includes('date') && ['where', 'eval'].includes(commandName); - const suggestions = filteredFieldsByType.concat( - displayDateSuggestions ? getDateLiterals() : [], functions ? getCompatibleFunctionDefinition(commandName, optionName, types, ignoreFn) : [], variables ? pushItUpInTheList(buildVariablesDefinitions(filteredVariablesByType), functions) @@ -1187,6 +1213,8 @@ async function getFunctionArgsSuggestions( ? refSignature.minParams - 1 > argIndex : false); + const shouldAddComma = hasMoreMandatoryArgs && fnDefinition.type !== 'builtin'; + const suggestedConstants = Array.from( new Set( fnDefinition.signatures.reduce<string[]>((acc, signature) => { @@ -1207,13 +1235,13 @@ async function getFunctionArgsSuggestions( ); if (suggestedConstants.length) { - return buildValueDefinitions(suggestedConstants).map((suggestion) => ({ - ...suggestion, - text: addCommaIf(hasMoreMandatoryArgs && fnDefinition.type !== 'builtin', suggestion.text), - })); + return buildValueDefinitions(suggestedConstants, { + addComma: hasMoreMandatoryArgs, + advanceCursorAndOpenSuggestions: hasMoreMandatoryArgs, + }); } - const suggestions = []; + const suggestions: SuggestionRawDefinition[] = []; const noArgDefined = !arg; const isUnknownColumn = arg && @@ -1267,7 +1295,9 @@ async function getFunctionArgsSuggestions( // if existing arguments are preset already, use them to filter out incompatible signatures .filter((signature) => { if (existingTypes.length) { - return existingTypes.every((type, index) => signature.params[index].type === type); + return existingTypes.every((type, index) => + compareTypesWithLiterals(signature.params[index].type, type) + ); } return true; }); @@ -1292,35 +1322,58 @@ async function getFunctionArgsSuggestions( // const [constantOnlyParamDefs, paramDefsWhichSupportFields] = partition( allParamDefinitionsForThisPosition, - (paramDef) => paramDef.constantOnly || /_literal$/.test(paramDef.type) + (paramDef) => paramDef.constantOnly || /_literal$/.test(paramDef.type as string) ); const getTypesFromParamDefs = (paramDefs: FunctionParameter[]) => { return Array.from(new Set(paramDefs.map(({ type }) => type))); }; + // Literals + suggestions.push( + ...getCompatibleLiterals( + command.name, + getTypesFromParamDefs(constantOnlyParamDefs) as string[], + undefined, + { addComma: shouldAddComma, advanceCursorAndOpenSuggestions: hasMoreMandatoryArgs } + ) + ); + + // Fields suggestions.push( - ...getCompatibleLiterals(command.name, getTypesFromParamDefs(constantOnlyParamDefs)) + ...pushItUpInTheList( + await getFieldsByType(getTypesFromParamDefs(paramDefsWhichSupportFields) as string[], [], { + addComma: shouldAddComma, + advanceCursorAndOpenSuggestions: hasMoreMandatoryArgs, + }), + true + ) ); + // Functions suggestions.push( - ...(await getFieldsOrFunctionsSuggestions( - getTypesFromParamDefs(paramDefsWhichSupportFields), + ...getCompatibleFunctionDefinition( command.name, option?.name, - getFieldsByType, - { - functions: true, - fields: true, - variables: variablesExcludingCurrentCommandOnes, - }, - // do not repropose the same function as arg - // i.e. avoid cases like abs(abs(abs(...))) with suggestions - { - ignoreFn: fnToIgnore, - } - )) + getTypesFromParamDefs(paramDefsWhichSupportFields) as string[], + fnToIgnore + ).map((suggestion) => ({ + ...suggestion, + text: addCommaIf(shouldAddComma, suggestion.text), + })) ); + + // could also be in stats (bucket) but our autocomplete is not great yet + if ( + getTypesFromParamDefs(paramDefsWhichSupportFields).includes('date') && + ['where', 'eval'].includes(command.name) + ) + suggestions.push( + ...getDateLiterals({ + addComma: shouldAddComma, + advanceCursorAndOpenSuggestions: hasMoreMandatoryArgs, + }) + ); } // for eval and row commands try also to complete numeric literals with time intervals where possible @@ -1329,18 +1382,10 @@ async function getFunctionArgsSuggestions( if (isLiteralItem(arg) && isNumericType(arg.literalType)) { // ... | EVAL fn(2 <suggest>) suggestions.push( - ...(await getFieldsOrFunctionsSuggestions( - ['time_literal_unit'], - command.name, - option?.name, - getFieldsByType, - { - functions: false, - fields: false, - variables: variablesExcludingCurrentCommandOnes, - literals: true, - } - )) + ...getCompatibleLiterals(command.name, ['time_literal_unit'], undefined, { + addComma: shouldAddComma, + advanceCursorAndOpenSuggestions: hasMoreMandatoryArgs, + }) ); } } @@ -1349,24 +1394,9 @@ async function getFunctionArgsSuggestions( // 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, - text: - hasMoreMandatoryArgs && fnDefinition.type !== 'builtin' - ? `${suggestion.text},` - : suggestion.text, - } - : suggestion - ); } - return suggestions.map(({ text, ...rest }) => ({ - ...rest, - text: addCommaIf(hasMoreMandatoryArgs && fnDefinition.type !== 'builtin' && text !== '', text), - })); + return suggestions; } async function getListArgsSuggestions( @@ -1406,7 +1436,7 @@ async function getListArgsSuggestions( const otherArgs = node.args.filter(Array.isArray).flat().filter(isColumnItem); suggestions.push( ...(await getFieldsOrFunctionsSuggestions( - [argType], + [argType as string], command.name, undefined, getFieldsByType, @@ -1521,7 +1551,7 @@ async function getOptionArgsSuggestions( innerText ); - if (isNewExpression || findPreviousWord(innerText) === 'WITH') { + if (isNewExpression || noCaseCompare(findPreviousWord(innerText), 'WITH')) { suggestions.push(buildNewVarDefinition(findNewVariable(anyEnhancedVariables))); } @@ -1595,19 +1625,6 @@ async function getOptionArgsSuggestions( } if (command.name === 'stats') { - suggestions.push( - ...(await getFieldsOrFunctionsSuggestions( - ['column'], - command.name, - option.name, - getFieldsByType, - { - functions: false, - fields: true, - } - )) - ); - const argDef = optionDef?.signature.params[argIndex]; const nodeArgType = extractFinalTypeFromArg(nodeArg, references); @@ -1623,7 +1640,7 @@ async function getOptionArgsSuggestions( option, { type: argDef?.type || 'any' }, nodeArg, - nodeArgType, + nodeArgType as string, references, getFieldsByType )) @@ -1667,20 +1684,27 @@ async function getOptionArgsSuggestions( }) ); } else if (isNewExpression || (isAssignment(nodeArg) && !isAssignmentComplete(nodeArg))) { - // Otherwise try to complete the expression suggesting some columns suggestions.push( - ...(await getFieldsOrFunctionsSuggestions( - types[0] === 'column' ? ['any'] : types, - command.name, - option.name, - getFieldsByType, - { - functions: option.name === 'by', - fields: true, - } - )) + ...(await getFieldsByType(types[0] === 'column' ? ['any'] : types, [], { + advanceCursorAndOpenSuggestions: true, + })) ); + if (option.name === 'by') { + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions( + types[0] === 'column' ? ['any'] : types, + command.name, + option.name, + getFieldsByType, + { + functions: true, + fields: false, + } + )) + ); + } + if (command.name === 'stats' && isNewExpression) { suggestions.push(buildNewVarDefinition(findNewVariable(anyVariables))); } diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts index e0ab600aa1382..640f62430f5df 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts @@ -16,6 +16,7 @@ import { TRIGGER_SUGGESTION_COMMAND, buildConstantsDefinitions, } from './factories'; +import { FunctionParameterType, FunctionReturnType } from '../definitions/types'; export function getAssignmentDefinitionCompletitionItem() { const assignFn = builtinFunctions.find(({ name }) => name === '=')!; @@ -54,8 +55,8 @@ export const getNextTokenForNot = ( export const getBuiltinCompatibleFunctionDefinition = ( command: string, option: string | undefined, - argType: string, - returnTypes?: string[], + argType: FunctionParameterType, + returnTypes?: FunctionReturnType[], { skipAssign }: { skipAssign?: boolean } = {} ): SuggestionRawDefinition[] => { const compatibleFunctions = builtinFunctions.filter( @@ -81,9 +82,9 @@ export const getBuiltinCompatibleFunctionDefinition = ( .map(getSuggestionBuiltinDefinition); }; -export const commandAutocompleteDefinitions: SuggestionRawDefinition[] = getAllCommands().map( - getSuggestionCommandDefinition -); +export const commandAutocompleteDefinitions: SuggestionRawDefinition[] = getAllCommands() + .filter(({ hidden }) => !hidden) + .map(getSuggestionCommandDefinition); function buildCharCompleteItem( label: string, @@ -98,13 +99,16 @@ function buildCharCompleteItem( sortText, }; } -export const pipeCompleteItem = buildCharCompleteItem( - '|', - i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.pipeDoc', { +export const pipeCompleteItem: SuggestionRawDefinition = { + label: '|', + text: '| ', + kind: 'Keyword', + detail: i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.pipeDoc', { defaultMessage: 'Pipe (|)', }), - { sortText: 'C', quoted: false } -); + sortText: 'C', + command: TRIGGER_SUGGESTION_COMMAND, +}; export const commaCompleteItem = buildCharCompleteItem( ',', diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts index cb56f40a6d6f4..c9d0185d5c301 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts @@ -102,7 +102,8 @@ export const getCompatibleFunctionDefinition = ( return fnSupportedByCommand .filter((mathDefinition) => mathDefinition.signatures.some( - (signature) => returnTypes[0] === 'any' || returnTypes.includes(signature.returnType) + (signature) => + returnTypes[0] === 'any' || returnTypes.includes(signature.returnType as string) ) ) .map(getSuggestionFunctionDefinition); @@ -130,7 +131,8 @@ export function getSuggestionCommandDefinition( } export const buildFieldsDefinitionsWithMetadata = ( - fields: ESQLRealField[] + fields: ESQLRealField[], + options?: { advanceCursorAndOpenSuggestions?: boolean; addComma?: boolean } ): SuggestionRawDefinition[] => { return fields.map((field) => { const description = field.metadata?.description; @@ -138,7 +140,10 @@ export const buildFieldsDefinitionsWithMetadata = ( const titleCaseType = field.type.charAt(0).toUpperCase() + field.type.slice(1); return { label: field.name, - text: getSafeInsertText(field.name), + text: + getSafeInsertText(field.name) + + (options?.addComma ? ',' : '') + + (options?.advanceCursorAndOpenSuggestions ? ' ' : ''), kind: 'Variable', detail: titleCaseType, documentation: description @@ -151,6 +156,7 @@ ${description}`, : undefined, // If there is a description, it is a field from ECS, so it should be sorted to the top sortText: description ? '1D' : 'D', + command: options?.advanceCursorAndOpenSuggestions ? TRIGGER_SUGGESTION_COMMAND : undefined, }; }); }; @@ -185,9 +191,8 @@ export const buildSourcesDefinitions = ( ): SuggestionRawDefinition[] => sources.map(({ name, isIntegration, title }) => ({ label: title ?? name, - text: getSafeInsertSourceText(name), + text: getSafeInsertSourceText(name) + (!isIntegration ? ' ' : ''), isSnippet: isIntegration, - ...(isIntegration && { command: TRIGGER_SUGGESTION_COMMAND }), kind: isIntegration ? 'Class' : 'Issue', detail: isIntegration ? i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.integrationDefinition', { @@ -197,16 +202,24 @@ export const buildSourcesDefinitions = ( defaultMessage: `Index`, }), sortText: 'A', + command: TRIGGER_SUGGESTION_COMMAND, })); export const buildConstantsDefinitions = ( userConstants: string[], detail?: string, - sortText?: string + sortText?: string, + /** + * Whether or not to advance the cursor and open the suggestions dialog after inserting the constant. + */ + options?: { advanceCursorAndOpenSuggestions?: boolean; addComma?: boolean } ): SuggestionRawDefinition[] => userConstants.map((label) => ({ label, - text: label, + text: + label + + (options?.addComma ? ',' : '') + + (options?.advanceCursorAndOpenSuggestions ? ' ' : ''), kind: 'Constant', detail: detail ?? @@ -214,32 +227,35 @@ export const buildConstantsDefinitions = ( defaultMessage: `Constant`, }), sortText: sortText ?? 'A', + command: options?.advanceCursorAndOpenSuggestions ? TRIGGER_SUGGESTION_COMMAND : undefined, })); export const buildValueDefinitions = ( values: string[], - detail?: string + options?: { advanceCursorAndOpenSuggestions?: boolean; addComma?: boolean } ): SuggestionRawDefinition[] => values.map((value) => ({ label: `"${value}"`, - text: `"${value}"`, - detail: - detail ?? - i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.valueDefinition', { - defaultMessage: 'Literal value', - }), + text: `"${value}"${options?.addComma ? ',' : ''}${ + options?.advanceCursorAndOpenSuggestions ? ' ' : '' + }`, + detail: i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.valueDefinition', { + defaultMessage: 'Literal value', + }), kind: 'Value', + command: options?.advanceCursorAndOpenSuggestions ? TRIGGER_SUGGESTION_COMMAND : undefined, })); export const buildNewVarDefinition = (label: string): SuggestionRawDefinition => { return { label, - text: `${label} =`, + text: `${label} = `, kind: 'Variable', detail: i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.newVarDoc', { defaultMessage: 'Define a new variable', }), sortText: '1', + command: TRIGGER_SUGGESTION_COMMAND, }; }; @@ -358,21 +374,39 @@ export function getUnitDuration(unit: number = 1) { * "magical" logic. Maybe this is really the same thing as the literalOptions parameter * definition property... */ -export function getCompatibleLiterals(commandName: string, types: string[], names?: string[]) { +export function getCompatibleLiterals( + commandName: string, + types: string[], + names?: string[], + options?: { advanceCursorAndOpenSuggestions?: boolean; addComma?: boolean } +) { const suggestions: SuggestionRawDefinition[] = []; if (types.some(isNumericType)) { if (commandName === 'limit') { // suggest 10/100/1000 for limit - suggestions.push(...buildConstantsDefinitions(['10', '100', '1000'], '')); + suggestions.push( + ...buildConstantsDefinitions(['10', '100', '1000'], '', undefined, { + advanceCursorAndOpenSuggestions: true, + }) + ); } } if (types.includes('time_literal')) { // filter plural for now and suggest only unit + singular - suggestions.push(...buildConstantsDefinitions(getUnitDuration(1))); // i.e. 1 year + suggestions.push( + ...buildConstantsDefinitions(getUnitDuration(1), undefined, undefined, options) + ); // 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(timeUnitsToSuggest.map(({ name }) => name))); // i.e. year, month, ... + suggestions.push( + ...buildConstantsDefinitions( + timeUnitsToSuggest.map(({ name }) => name), + undefined, + undefined, + options + ) + ); // i.e. year, month, ... } if (types.includes('string')) { if (names) { @@ -383,25 +417,31 @@ export function getCompatibleLiterals(commandName: string, types: string[], name [commandName === 'grok' ? '"%{WORD:firstWord}"' : '"%{firstWord}"'], i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.aPatternString', { defaultMessage: 'A pattern string', - }) + }), + undefined, + options ) ); } else { - suggestions.push(...buildConstantsDefinitions(['string'], '')); + suggestions.push(...buildConstantsDefinitions(['string'], '', undefined, options)); } } } return suggestions; } -export function getDateLiterals() { +export function getDateLiterals(options?: { + advanceCursorAndOpenSuggestions?: boolean; + addComma?: boolean; +}) { return [ ...buildConstantsDefinitions( TIME_SYSTEM_PARAMS, i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.namedParamDefinition', { defaultMessage: 'Named parameter', }), - '1A' + '1A', + options ), { label: i18n.translate( diff --git a/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts b/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts index a4ff739297201..cca1197ed629b 100644 --- a/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts @@ -11,32 +11,39 @@ import { validateQuery } from '../validation/validation'; import { getAllFunctions } from '../shared/helpers'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; import { CodeActionOptions } from './types'; +import { ESQLRealField } from '../validation/types'; +import { FieldType } from '../definitions/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' }, - ] - ), + getFieldsFor: jest.fn<Promise<ESQLRealField[]>, any>(async ({ query }) => { + if (/enrich/.test(query)) { + const fields: ESQLRealField[] = [ + { name: 'otherField', type: 'keyword' }, + { name: 'yetAnotherField', type: 'double' }, + ]; + return fields; + } + + if (/unsupported_index/.test(query)) { + const fields: ESQLRealField[] = [{ name: 'unsupported_field', type: 'unsupported' }]; + return fields; + } + + const localDataTypes: FieldType[] = ['keyword', 'double', 'date', 'boolean', 'ip']; + const fields: ESQLRealField[] = [ + ...localDataTypes.map((type) => ({ + name: `${type}Field`, + type, + })), + { name: 'geoPointField', type: 'geo_point' }, + { name: 'any#Char$Field', type: 'double' }, + { name: 'kubernetes.something.something', type: 'double' }, + { name: '@timestamp', type: 'date' }, + ]; + + return fields; + }), getSources: jest.fn(async () => ['index', '.secretIndex', 'my-index'].map((name) => ({ name, @@ -162,29 +169,29 @@ describe('quick fixes logic', () => { { relaxOnMissingCallbacks: false }, ]) { for (const command of ['KEEP', 'DROP', 'EVAL']) { - testQuickFixes(`FROM index | ${command} stringField`, [], options); - // strongField => stringField - testQuickFixes(`FROM index | ${command} strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ${command} keywordField`, [], options); + // koywordField => keywordField + testQuickFixes(`FROM index | ${command} koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ${command} numberField, strongField`, - ['stringField'], + `FROM index | ${command} numberField, koywordField`, + ['keywordField'], options ); } - testQuickFixes(`FROM index | EVAL round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | EVAL var0 = round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE round(strongField) > 0`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE 0 < round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | RENAME strongField as newField`, ['stringField'], options); + testQuickFixes(`FROM index | EVAL round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | EVAL var0 = round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE round(koywordField) > 0`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE 0 < round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | RENAME koywordField as newField`, ['keywordField'], options); // This levarage the knowledge of the enrich policy fields to suggest the right field testQuickFixes( `FROM index | ENRICH policy | KEEP yetAnotherField2`, ['yetAnotherField'], options ); - testQuickFixes(`FROM index | ENRICH policy ON strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ENRICH policy ON koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ENRICH policy ON stringField WITH yetAnotherField2`, + `FROM index | ENRICH policy ON keywordField WITH yetAnotherField2`, ['yetAnotherField'], options ); @@ -209,29 +216,29 @@ describe('quick fixes logic', () => { { relaxOnMissingCallbacks: false }, ]) { for (const command of ['KEEP', 'DROP', 'EVAL']) { - testQuickFixes(`FROM index | ${command} stringField`, [], options); - // strongField => stringField - testQuickFixes(`FROM index | ${command} strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ${command} keywordField`, [], options); + // koywordField => keywordField + testQuickFixes(`FROM index | ${command} koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ${command} numberField, strongField`, - ['stringField'], + `FROM index | ${command} numberField, koywordField`, + ['keywordField'], options ); } - testQuickFixes(`FROM index | EVAL round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | EVAL var0 = round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE round(strongField) > 0`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE 0 < round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | RENAME strongField as newField`, ['stringField'], options); + testQuickFixes(`FROM index | EVAL round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | EVAL var0 = round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE round(koywordField) > 0`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE 0 < round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | RENAME koywordField as newField`, ['keywordField'], options); // This levarage the knowledge of the enrich policy fields to suggest the right field testQuickFixes( `FROM index | ENRICH policy | KEEP yetAnotherField2`, ['yetAnotherField'], options ); - testQuickFixes(`FROM index | ENRICH policy ON strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ENRICH policy ON koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ENRICH policy ON stringField WITH yetAnotherField2`, + `FROM index | ENRICH policy ON keywordField WITH yetAnotherField2`, ['yetAnotherField'], options ); @@ -329,8 +336,8 @@ describe('quick fixes logic', () => { { relaxOnMissingCallbacks: false }, { relaxOnMissingCallbacks: false }, ]) { - testQuickFixes(`FROM index | WHERE stringField like 'asda'`, ['"asda"'], options); - testQuickFixes(`FROM index | WHERE stringField not like 'asda'`, ['"asda"'], options); + testQuickFixes(`FROM index | WHERE keywordField like 'asda'`, ['"asda"'], options); + testQuickFixes(`FROM index | WHERE keywordField not like 'asda'`, ['"asda"'], options); } }); @@ -407,7 +414,7 @@ describe('quick fixes logic', () => { describe('callbacks', () => { it('should not crash if specific callback functions are not passed', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, @@ -427,7 +434,7 @@ describe('quick fixes logic', () => { it('should not crash if specific callback functions are not passed with relaxed option', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, @@ -453,7 +460,7 @@ describe('quick fixes logic', () => { it('should not crash no callbacks are passed', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, @@ -469,7 +476,7 @@ describe('quick fixes logic', () => { it('should not crash no callbacks are passed with relaxed option', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts index 97dc654f525ce..262d7a2fc7b32 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/aggs.ts @@ -31,7 +31,7 @@ function createNumericAggDefinition({ name, type: 'agg', description, - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ ...ESQL_NUMBER_TYPES.map((numericType) => ({ params: [ @@ -105,7 +105,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ } ), type: 'agg', - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ ...ESQL_COMMON_NUMERIC_TYPES.map((numericType: FunctionParameterType) => { return ESQL_COMMON_NUMERIC_TYPES.map((weightType: FunctionParameterType) => ({ @@ -133,7 +133,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ defaultMessage: 'Returns the maximum value in a field.', }), type: 'agg', - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ ...ESQL_COMMON_NUMERIC_TYPES.map((type) => ({ params: [{ name: 'column', type, noNestingFunctions: true }], @@ -164,7 +164,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ defaultMessage: 'Returns the minimum value in a field.', }), type: 'agg', - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ ...ESQL_COMMON_NUMERIC_TYPES.map((type) => ({ params: [{ name: 'column', type, noNestingFunctions: true }], @@ -197,7 +197,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.countDoc', { defaultMessage: 'Returns the count of the values in a field.', }), - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ { params: [ @@ -223,7 +223,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ defaultMessage: 'Returns the count of distinct values in a field.', } ), - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ { params: [ @@ -252,7 +252,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ defaultMessage: 'Returns the count of distinct values in a field.', } ), - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ { params: [{ name: 'column', type: 'cartesian_point', noNestingFunctions: true }], @@ -338,7 +338,7 @@ export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ 'An aggregation that computes the weighted average of numeric values that are extracted from the aggregated documents.', } ), - supportedCommands: ['stats', 'metrics'], + supportedCommands: ['stats', 'inlinestats', 'metrics'], signatures: [ ...ESQL_COMMON_NUMERIC_TYPES.map((numericType: FunctionParameterType) => { return ESQL_COMMON_NUMERIC_TYPES.map((weightType: FunctionParameterType) => ({ diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts index 10945d1c6b135..d3a510ff3afad 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts @@ -308,13 +308,13 @@ const comparisonFunctions: FunctionDefinition[] = [ { params: [ { name: 'left', type: 'boolean' as const }, - { name: 'right', type: 'string' as const, constantOnly: true }, + { name: 'right', type: 'keyword' as const, constantOnly: true }, ], returnType: 'boolean' as const, }, { params: [ - { name: 'left', type: 'string' as const, constantOnly: true }, + { name: 'left', type: 'keyword' as const, constantOnly: true }, { name: 'right', type: 'boolean' as const }, ], returnType: 'boolean' as const, @@ -338,13 +338,13 @@ const comparisonFunctions: FunctionDefinition[] = [ { params: [ { name: 'left', type: 'boolean' as const }, - { name: 'right', type: 'string' as const, constantOnly: true }, + { name: 'right', type: 'keyword' as const, constantOnly: true }, ], returnType: 'boolean' as const, }, { params: [ - { name: 'left', type: 'string' as const, constantOnly: true }, + { name: 'left', type: 'keyword' as const, constantOnly: true }, { name: 'right', type: 'boolean' as const }, ], returnType: 'boolean' as const, @@ -591,7 +591,16 @@ const otherDefinitions: FunctionDefinition[] = [ description: i18n.translate('kbn-esql-validation-autocomplete.esql.definition.assignDoc', { defaultMessage: 'Assign (=)', }), - supportedCommands: ['eval', 'stats', 'metrics', 'row', 'dissect', 'where', 'enrich'], + supportedCommands: [ + 'eval', + 'stats', + 'inlinestats', + 'metrics', + 'row', + 'dissect', + 'where', + 'enrich', + ], supportedOptions: ['by', 'with'], signatures: [ { diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts index df0d5f02f8cf0..f97910c5c75bd 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/commands.ts @@ -32,6 +32,121 @@ import { } from './options'; import type { CommandDefinition } from './types'; +const statsValidator = (command: ESQLCommand) => { + const messages: ESQLMessage[] = []; + const commandName = command.name.toUpperCase(); + if (!command.args.length) { + messages.push({ + location: command.location, + text: i18n.translate('kbn-esql-validation-autocomplete.esql.validation.statsNoArguments', { + defaultMessage: + 'At least one aggregation or grouping expression required in [{commandName}]', + values: { commandName }, + }), + type: 'error', + code: 'statsNoArguments', + }); + } + + // now that all functions are supported, there's a specific check to perform + // unfortunately the logic here is a bit complex as it needs to dig deeper into the args + // until an agg function is detected + // in the long run this might be integrated into the validation function + const statsArg = command.args + .flatMap((arg) => (isAssignment(arg) ? arg.args[1] : arg)) + .filter(isFunctionItem); + + if (statsArg.length) { + function isAggFunction(arg: ESQLAstItem): arg is ESQLFunction { + return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type === 'agg'; + } + function isOtherFunction(arg: ESQLAstItem): arg is ESQLFunction { + return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type !== 'agg'; + } + + function checkAggExistence(arg: ESQLFunction): boolean { + // TODO the grouping function check may not + // hold true for all future cases + if (isAggFunction(arg)) { + return true; + } + if (isOtherFunction(arg)) { + return (arg as ESQLFunction).args.filter(isFunctionItem).some(checkAggExistence); + } + return false; + } + // first check: is there an agg function somewhere? + const noAggsExpressions = statsArg.filter((arg) => !checkAggExistence(arg)); + + if (noAggsExpressions.length) { + messages.push( + ...noAggsExpressions.map((fn) => ({ + location: fn.location, + text: i18n.translate( + 'kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction', + { + defaultMessage: + 'At least one aggregation function required in [{commandName}], found [{expression}]', + values: { + expression: fn.text, + commandName, + }, + } + ), + type: 'error' as const, + code: 'statsNoAggFunction', + })) + ); + } else { + function isConstantOrAggFn(arg: ESQLAstItem): boolean { + return isLiteralItem(arg) || isAggFunction(arg); + } + // now check that: + // * the agg function is at root level + // * or if it's a builtin function, then all operands are agg functions or literals + // * or if it's a eval function then all arguments are agg functions or literals + function checkFunctionContent(arg: ESQLFunction) { + // TODO the grouping function check may not + // hold true for all future cases + if (isAggFunction(arg)) { + return true; + } + return (arg as ESQLFunction).args.every( + (subArg): boolean => + isConstantOrAggFn(subArg) || + (isOtherFunction(subArg) ? checkFunctionContent(subArg) : false) + ); + } + // @TODO: improve here the check to get the last instance of the invalidExpression + // to provide a better location for the error message + // i.e. STATS round(round(round( a + sum(b) ))) + // should return the location of the + node, just before the agg one + const invalidExpressions = statsArg.filter((arg) => !checkFunctionContent(arg)); + + if (invalidExpressions.length) { + messages.push( + ...invalidExpressions.map((fn) => ({ + location: fn.location, + text: i18n.translate( + 'kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues', + { + defaultMessage: + 'Cannot combine aggregation and non-aggregation values in [{commandName}], found [{expression}]', + values: { + expression: fn.text, + commandName, + }, + } + ), + type: 'error' as const, + code: 'statsNoCombinationOfAggAndNonAggValues', + })) + ); + } + } + } + return messages; +}; export const commandDefinitions: CommandDefinition[] = [ { name: 'row', @@ -118,120 +233,29 @@ export const commandDefinitions: CommandDefinition[] = [ }, options: [byOption], modes: [], - validate: (command: ESQLCommand) => { - const messages: ESQLMessage[] = []; - if (!command.args.length) { - messages.push({ - location: command.location, - text: i18n.translate( - 'kbn-esql-validation-autocomplete.esql.validation.statsNoArguments', - { - defaultMessage: 'At least one aggregation or grouping expression required in [STATS]', - } - ), - type: 'error', - code: 'statsNoArguments', - }); - } - - // now that all functions are supported, there's a specific check to perform - // unfortunately the logic here is a bit complex as it needs to dig deeper into the args - // until an agg function is detected - // in the long run this might be integrated into the validation function - const statsArg = command.args - .flatMap((arg) => (isAssignment(arg) ? arg.args[1] : arg)) - .filter(isFunctionItem); - - if (statsArg.length) { - function isAggFunction(arg: ESQLAstItem): arg is ESQLFunction { - return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type === 'agg'; - } - function isOtherFunction(arg: ESQLAstItem): arg is ESQLFunction { - return isFunctionItem(arg) && getFunctionDefinition(arg.name)?.type !== 'agg'; - } - - function checkAggExistence(arg: ESQLFunction): boolean { - // TODO the grouping function check may not - // hold true for all future cases - if (isAggFunction(arg)) { - return true; - } - if (isOtherFunction(arg)) { - return (arg as ESQLFunction).args.filter(isFunctionItem).some(checkAggExistence); - } - return false; - } - // first check: is there an agg function somewhere? - const noAggsExpressions = statsArg.filter((arg) => !checkAggExistence(arg)); - - if (noAggsExpressions.length) { - messages.push( - ...noAggsExpressions.map((fn) => ({ - location: fn.location, - text: i18n.translate( - 'kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction', - { - defaultMessage: - 'At least one aggregation function required in [STATS], found [{expression}]', - values: { - expression: fn.text, - }, - } - ), - type: 'error' as const, - code: 'statsNoAggFunction', - })) - ); - } else { - function isConstantOrAggFn(arg: ESQLAstItem): boolean { - return isLiteralItem(arg) || isAggFunction(arg); - } - // now check that: - // * the agg function is at root level - // * or if it's a builtin function, then all operands are agg functions or literals - // * or if it's a eval function then all arguments are agg functions or literals - function checkFunctionContent(arg: ESQLFunction) { - // TODO the grouping function check may not - // hold true for all future cases - if (isAggFunction(arg)) { - return true; - } - return (arg as ESQLFunction).args.every( - (subArg): boolean => - isConstantOrAggFn(subArg) || - (isOtherFunction(subArg) ? checkFunctionContent(subArg) : false) - ); - } - // @TODO: improve here the check to get the last instance of the invalidExpression - // to provide a better location for the error message - // i.e. STATS round(round(round( a + sum(b) ))) - // should return the location of the + node, just before the agg one - const invalidExpressions = statsArg.filter((arg) => !checkFunctionContent(arg)); - - if (invalidExpressions.length) { - messages.push( - ...invalidExpressions.map((fn) => ({ - location: fn.location, - text: i18n.translate( - 'kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues', - { - defaultMessage: - 'Cannot combine aggregation and non-aggregation values in [STATS], found [{expression}]', - values: { - expression: fn.text, - }, - } - ), - type: 'error' as const, - code: 'statsNoCombinationOfAggAndNonAggValues', - })) - ); - } - } + validate: statsValidator, + }, + { + name: 'inlinestats', + hidden: true, + description: i18n.translate( + 'kbn-esql-validation-autocomplete.esql.definitions.inlineStatsDoc', + { + defaultMessage: + 'Calculates an aggregate result and merges that result back into the stream of input data. Without the optional `BY` clause this will produce a single result which is appended to each row. With a `BY` clause this will produce one result per grouping and merge the result into the stream based on matching group keys.', } - return messages; + ), + examples: ['… | EVAL bar = a * b | INLINESTATS m = MAX(bar) BY b'], + signature: { + multipleParams: true, + params: [{ name: 'expression', type: 'function', optional: true }], }, + options: [byOption], + modes: [], + // Reusing the same validation logic as stats command + validate: statsValidator, }, + { name: 'eval', description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.evalDoc', { diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts index 3b9d9131e7643..14626048ced92 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/functions.ts @@ -80,7 +80,7 @@ const absDefinition: FunctionDefinition = { returnType: 'unsigned_long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -139,7 +139,7 @@ const acosDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=.9\n| EVAL acos=ACOS(a)'], @@ -196,7 +196,7 @@ const asinDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=.9\n| EVAL asin=ASIN(a)'], @@ -253,7 +253,7 @@ const atanDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=12.9\n| EVAL atan=ATAN(a)'], @@ -510,7 +510,7 @@ const atan2Definition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW y=12.9, x=.6\n| EVAL atan2=ATAN2(y, x)'], @@ -567,7 +567,7 @@ const cbrtDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW d = 1000.0\n| EVAL c = cbrt(d)'], @@ -623,7 +623,7 @@ const ceilDefinition: FunctionDefinition = { returnType: 'unsigned_long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8\n| EVAL a=CEIL(a)'], @@ -672,7 +672,7 @@ const cidrMatchDefinition: FunctionDefinition = { minParams: 2, }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -938,7 +938,7 @@ const coalesceDefinition: FunctionDefinition = { minParams: 1, }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=null, b="b"\n| EVAL COALESCE(a, b)'], @@ -1018,7 +1018,7 @@ const concatDefinition: FunctionDefinition = { minParams: 2, }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -1076,7 +1076,7 @@ const cosDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8 \n| EVAL cos=COS(a)'], @@ -1132,7 +1132,7 @@ const coshDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8 \n| EVAL cosh=COSH(a)'], @@ -1250,7 +1250,7 @@ const dateDiffDefinition: FunctionDefinition = { returnType: 'integer', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -1330,7 +1330,7 @@ const dateExtractDefinition: FunctionDefinition = { returnType: 'long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -1379,7 +1379,7 @@ const dateFormatDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -1458,7 +1458,7 @@ const dateParseDefinition: FunctionDefinition = { returnType: 'date', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW date_string = "2022-05-06"\n| EVAL date = DATE_PARSE("yyyy-MM-dd", date_string)'], @@ -1504,7 +1504,7 @@ const dateTruncDefinition: FunctionDefinition = { returnType: 'date', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -1528,7 +1528,7 @@ const eDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW E()'], @@ -1559,6 +1559,36 @@ const endsWithDefinition: FunctionDefinition = { ], returnType: 'boolean', }, + { + params: [ + { + name: 'str', + type: 'keyword', + optional: false, + }, + { + name: 'suffix', + type: 'text', + optional: false, + }, + ], + returnType: 'boolean', + }, + { + params: [ + { + name: 'str', + type: 'text', + optional: false, + }, + { + name: 'suffix', + type: 'keyword', + optional: false, + }, + ], + returnType: 'boolean', + }, { params: [ { @@ -1575,7 +1605,7 @@ const endsWithDefinition: FunctionDefinition = { returnType: 'boolean', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['FROM employees\n| KEEP last_name\n| EVAL ln_E = ENDS_WITH(last_name, "d")'], @@ -1631,7 +1661,7 @@ const expDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW d = 5.0\n| EVAL s = EXP(d)'], @@ -1687,7 +1717,7 @@ const floorDefinition: FunctionDefinition = { returnType: 'unsigned_long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8\n| EVAL a=FLOOR(a)'], @@ -1723,7 +1753,7 @@ const fromBase64Definition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['row a = "ZWxhc3RpYw==" \n| eval d = from_base64(a)'], @@ -1923,7 +1953,7 @@ const greatestDefinition: FunctionDefinition = { minParams: 1, }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a = 10, b = 20\n| EVAL g = GREATEST(a, b)'], @@ -1959,7 +1989,7 @@ const ipPrefixDefinition: FunctionDefinition = { returnType: 'ip', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -2161,7 +2191,7 @@ const leastDefinition: FunctionDefinition = { minParams: 1, }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a = 10, b = 20\n| EVAL l = LEAST(a, b)'], @@ -2208,7 +2238,7 @@ const leftDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -2246,7 +2276,7 @@ const lengthDefinition: FunctionDefinition = { returnType: 'integer', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['FROM employees\n| KEEP first_name, last_name\n| EVAL fn_length = LENGTH(first_name)'], @@ -2403,7 +2433,7 @@ const locateDefinition: FunctionDefinition = { returnType: 'integer', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['row a = "hello"\n| eval a_ll = locate(a, "ll")'], @@ -2700,14 +2730,14 @@ const logDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: (fnDef: ESQLFunction) => { const messages = []; // do not really care here about the base and field // just need to check both values are not negative for (const arg of fnDef.args) { - if (isLiteralItem(arg) && Number(arg.value) < 0) { + if (isLiteralItem(arg) && typeof arg.value === 'number' && arg.value < 0) { messages.push({ type: 'warning' as const, code: 'logOfNegativeValue', @@ -2783,14 +2813,14 @@ const log10Definition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: (fnDef: ESQLFunction) => { const messages = []; // do not really care here about the base and field // just need to check both values are not negative for (const arg of fnDef.args) { - if (isLiteralItem(arg) && Number(arg.value) < 0) { + if (isLiteralItem(arg) && typeof arg.value === 'number' && arg.value < 0) { messages.push({ type: 'warning' as const, code: 'logOfNegativeValue', @@ -2842,7 +2872,7 @@ const ltrimDefinition: FunctionDefinition = { returnType: 'text', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -3055,7 +3085,7 @@ const mvAppendDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [], @@ -3112,7 +3142,7 @@ const mvAvgDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=[3, 5, 1, 6]\n| EVAL avg_a = MV_AVG(a)'], @@ -3189,7 +3219,7 @@ const mvConcatDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -3349,7 +3379,7 @@ const mvCountDefinition: FunctionDefinition = { returnType: 'integer', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=["foo", "zoo", "bar"]\n| EVAL count_a = MV_COUNT(a)'], @@ -3495,7 +3525,7 @@ const mvDedupeDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=["foo", "foo", "bar", "foo"]\n| EVAL dedupe_a = MV_DEDUPE(a)'], @@ -3652,7 +3682,7 @@ const mvFirstDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a="foo;bar;baz"\n| EVAL first_a = MV_FIRST(SPLIT(a, ";"))'], @@ -3809,7 +3839,7 @@ const mvLastDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a="foo;bar;baz"\n| EVAL last_a = MV_LAST(SPLIT(a, ";"))'], @@ -3926,7 +3956,7 @@ const mvMaxDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -3986,7 +4016,7 @@ const mvMedianDefinition: FunctionDefinition = { returnType: 'unsigned_long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -4106,7 +4136,7 @@ const mvMinDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -4115,6 +4145,43 @@ const mvMinDefinition: FunctionDefinition = { ], }; +// Do not edit this manually... generated by scripts/generate_function_definitions.ts +const mvPseriesWeightedSumDefinition: FunctionDefinition = { + type: 'eval', + name: 'mv_pseries_weighted_sum', + description: i18n.translate( + 'kbn-esql-validation-autocomplete.esql.definitions.mv_pseries_weighted_sum', + { + defaultMessage: + 'Converts a multivalued expression into a single-valued column by multiplying every element on the input list by its corresponding term in P-Series and computing the sum.', + } + ), + alias: undefined, + signatures: [ + { + params: [ + { + name: 'number', + type: 'double', + optional: false, + }, + { + name: 'p', + type: 'double', + optional: false, + }, + ], + returnType: 'double', + }, + ], + supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedOptions: ['by'], + validate: undefined, + examples: [ + 'ROW a = [70.0, 45.0, 21.0, 21.0, 21.0]\n| EVAL sum = MV_PSERIES_WEIGHTED_SUM(a, 1.5)\n| KEEP sum', + ], +}; + // Do not edit this manually... generated by scripts/generate_function_definitions.ts const mvSliceDefinition: FunctionDefinition = { type: 'eval', @@ -4386,7 +4453,7 @@ const mvSliceDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -4549,9 +4616,10 @@ const mvSortDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, + examples: ['ROW a = [4, 2, -3, 2]\n| EVAL sa = mv_sort(a), sd = mv_sort(a, "DESC")'], }; @@ -4606,7 +4674,7 @@ const mvSumDefinition: FunctionDefinition = { returnType: 'unsigned_long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=[3, 5, 6]\n| EVAL sum_a = MV_SUM(a)'], @@ -4843,7 +4911,7 @@ const mvZipDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -4865,7 +4933,7 @@ const nowDefinition: FunctionDefinition = { returnType: 'date', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW current_date = NOW()', 'FROM sample_data\n| WHERE @timestamp > NOW() - 1 hour'], @@ -4885,7 +4953,7 @@ const piDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW PI()'], @@ -5141,7 +5209,7 @@ const powDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -5191,7 +5259,7 @@ const repeatDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a = "Hello!"\n| EVAL triple_a = REPEAT(a, 3);'], @@ -5368,7 +5436,7 @@ const replaceDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW str = "Hello World"\n| EVAL str = REPLACE(str, "World", "Universe")\n| KEEP str'], @@ -5415,7 +5483,7 @@ const rightDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -5519,7 +5587,7 @@ const roundDefinition: FunctionDefinition = { returnType: 'unsigned_long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -5557,7 +5625,7 @@ const rtrimDefinition: FunctionDefinition = { returnType: 'text', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -5616,7 +5684,7 @@ const signumDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW d = 100.0\n| EVAL s = SIGNUM(d)'], @@ -5672,7 +5740,7 @@ const sinDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8 \n| EVAL sin=SIN(a)'], @@ -5728,7 +5796,7 @@ const sinhDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8 \n| EVAL sinh=SINH(a)'], @@ -5804,7 +5872,7 @@ const splitDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW words="foo;bar;baz;qux;quux;corge"\n| EVAL word = SPLIT(words, ";")'], @@ -5861,7 +5929,7 @@ const sqrtDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW d = 100.0\n| EVAL s = SQRT(d)'], @@ -5998,7 +6066,7 @@ const stContainsDefinition: FunctionDefinition = { returnType: 'boolean', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6137,7 +6205,7 @@ const stDisjointDefinition: FunctionDefinition = { returnType: 'boolean', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6186,7 +6254,7 @@ const stDistanceDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6325,7 +6393,7 @@ const stIntersectsDefinition: FunctionDefinition = { returnType: 'boolean', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6464,7 +6532,7 @@ const stWithinDefinition: FunctionDefinition = { returnType: 'boolean', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6503,7 +6571,7 @@ const stXDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6542,7 +6610,7 @@ const stYDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6575,6 +6643,36 @@ const startsWithDefinition: FunctionDefinition = { ], returnType: 'boolean', }, + { + params: [ + { + name: 'str', + type: 'keyword', + optional: false, + }, + { + name: 'prefix', + type: 'text', + optional: false, + }, + ], + returnType: 'boolean', + }, + { + params: [ + { + name: 'str', + type: 'text', + optional: false, + }, + { + name: 'prefix', + type: 'keyword', + optional: false, + }, + ], + returnType: 'boolean', + }, { params: [ { @@ -6591,7 +6689,7 @@ const startsWithDefinition: FunctionDefinition = { returnType: 'boolean', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['FROM employees\n| KEEP last_name\n| EVAL ln_S = STARTS_WITH(last_name, "B")'], @@ -6648,7 +6746,7 @@ const substringDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -6708,7 +6806,7 @@ const tanDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8 \n| EVAL tan=TAN(a)'], @@ -6764,7 +6862,7 @@ const tanhDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=1.8 \n| EVAL tanh=TANH(a)'], @@ -6784,7 +6882,7 @@ const tauDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW TAU()'], @@ -6820,7 +6918,7 @@ const toBase64Definition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['row a = "elastic" \n| eval e = to_base64(a)'], @@ -6907,7 +7005,7 @@ const toBooleanDefinition: FunctionDefinition = { returnType: 'boolean', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW str = ["true", "TRuE", "false", "", "yes", "1"]\n| EVAL bool = TO_BOOLEAN(str)'], @@ -6957,7 +7055,7 @@ const toCartesianpointDefinition: FunctionDefinition = { returnType: 'cartesian_point', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -7019,7 +7117,7 @@ const toCartesianshapeDefinition: FunctionDefinition = { returnType: 'cartesian_shape', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -7108,7 +7206,7 @@ const toDatetimeDefinition: FunctionDefinition = { returnType: 'date', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -7167,7 +7265,7 @@ const toDegreesDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW rad = [1.57, 3.14, 4.71]\n| EVAL deg = TO_DEGREES(rad)'], @@ -7294,7 +7392,7 @@ const toDoubleDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -7343,7 +7441,7 @@ const toGeopointDefinition: FunctionDefinition = { returnType: 'geo_point', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW wkt = "POINT(42.97109630194 14.7552534413725)"\n| EVAL pt = TO_GEOPOINT(wkt)'], @@ -7400,7 +7498,7 @@ const toGeoshapeDefinition: FunctionDefinition = { returnType: 'geo_shape', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -7509,7 +7607,7 @@ const toIntegerDefinition: FunctionDefinition = { returnType: 'integer', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW long = [5013792, 2147483647, 501379200000]\n| EVAL int = TO_INTEGER(long)'], @@ -7555,7 +7653,7 @@ const toIpDefinition: FunctionDefinition = { returnType: 'ip', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -7674,7 +7772,7 @@ const toLongDefinition: FunctionDefinition = { returnType: 'long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -7712,7 +7810,7 @@ const toLowerDefinition: FunctionDefinition = { returnType: 'text', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW message = "Some Text"\n| EVAL message_lower = TO_LOWER(message)'], @@ -7768,7 +7866,7 @@ const toRadiansDefinition: FunctionDefinition = { returnType: 'double', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW deg = [90.0, 180.0, 270.0]\n| EVAL rad = TO_RADIANS(deg)'], @@ -7924,7 +8022,7 @@ const toStringDefinition: FunctionDefinition = { returnType: 'keyword', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW a=10\n| EVAL j = TO_STRING(a)', 'ROW a=[10, 9, 8]\n| EVAL j = TO_STRING(a)'], @@ -8024,7 +8122,7 @@ const toUnsignedLongDefinition: FunctionDefinition = { returnType: 'unsigned_long', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -8062,7 +8160,7 @@ const toUpperDefinition: FunctionDefinition = { returnType: 'text', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW message = "Some Text"\n| EVAL message_upper = TO_UPPER(message)'], @@ -8108,7 +8206,7 @@ const toVersionDefinition: FunctionDefinition = { returnType: 'version', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: ['ROW v = TO_VERSION("1.2.3")'], @@ -8144,7 +8242,7 @@ const trimDefinition: FunctionDefinition = { returnType: 'text', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -8177,7 +8275,7 @@ const caseDefinition: FunctionDefinition = { returnType: 'any', }, ], - supportedCommands: ['stats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], validate: undefined, examples: [ @@ -8226,6 +8324,7 @@ export const evalFunctionDefinitions = [ mvMaxDefinition, mvMedianDefinition, mvMinDefinition, + mvPseriesWeightedSumDefinition, mvSliceDefinition, mvSortDefinition, mvSumDefinition, diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts index a44db712ab230..3773a7c30e3d1 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { CommandDefinition, FunctionDefinition } from './types'; +import type { CommandDefinition, FunctionDefinition, FunctionParameterType } from './types'; /** * Given a function definition, this function will return a list of function signatures @@ -104,7 +104,7 @@ export function printArguments( optional, }: { name: string; - type: string | string[]; + type: FunctionParameterType | FunctionParameterType[]; optional?: boolean; }, withTypes: boolean diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts index 660bb1c7aca81..2e8c32b5be003 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts @@ -8,42 +8,67 @@ import type { ESQLCommand, ESQLCommandOption, ESQLFunction, ESQLMessage } from '@kbn/esql-ast'; -// Currently, partial of the full list -// https://github.com/elastic/elasticsearch/blob/main/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java -export const supportedFieldTypes = [ +/** + * All supported field types in ES|QL. This is all the types + * that can come back in the table from a query. + */ +export const fieldTypes = [ + 'boolean', + 'date', 'double', - 'unsigned_long', - 'long', + 'ip', + 'keyword', 'integer', - 'counter_integer', - 'counter_long', - 'counter_double', - 'date', - 'date_period', + 'long', 'text', - 'keyword', - 'boolean', - 'ip', + 'unsigned_long', + 'version', 'cartesian_point', 'cartesian_shape', 'geo_point', 'geo_shape', - 'version', + 'counter_integer', + 'counter_long', + 'counter_double', + 'unsupported', +] as const; + +export type FieldType = (typeof fieldTypes)[number]; + +export const isFieldType = (type: string | FunctionParameterType): type is FieldType => + fieldTypes.includes(type as FieldType); + +/** + * This is the list of all data types that are supported in ES|QL. + * + * Not all of these can be used as field types. Some can only be literals, + * others may be the value of a field, but cannot be used in the index mapping. + * + * This is a partial list. The full list is here and we may need to expand this type as + * the capabilities of the client-side engines grow. + * https://github.com/elastic/elasticsearch/blob/main/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java + */ +export const dataTypes = [ + ...fieldTypes, + 'null', + 'time_literal', // @TODO consider merging time_literal with time_duration + 'time_duration', + 'date_period', ] as const; -export const isSupportedFieldType = (type: string): type is SupportedFieldType => - supportedFieldTypes.includes(type as SupportedFieldType); +export type SupportedDataType = (typeof dataTypes)[number]; -export type SupportedFieldType = (typeof supportedFieldTypes)[number]; +export const isSupportedDataType = ( + type: string | FunctionParameterType +): type is SupportedDataType => dataTypes.includes(type as SupportedDataType); -export type FunctionParameterType = - | SupportedFieldType - | 'string' - | 'null' - | 'any' - | 'chrono_literal' - | 'time_literal' - | 'time_duration' +/** + * This is a set of array types. These aren't official ES|QL types, but they are + * currently used in the function definitions in a couple of specific scenarios. + * + * The fate of these is uncertain. They may be removed in the future. + */ +type ArrayType = | 'double[]' | 'unsigned_long[]' | 'long[]' @@ -51,38 +76,22 @@ export type FunctionParameterType = | 'counter_integer[]' | 'counter_long[]' | 'counter_double[]' - | 'string[]' | 'keyword[]' | 'text[]' | 'boolean[]' | 'any[]' - | 'datetime[]' + | 'date[]' | 'date_period[]'; -export type FunctionReturnType = - | 'double' - | 'unsigned_long' - | 'long' - | 'integer' - | 'int' - | 'counter_integer' - | 'counter_long' - | 'counter_double' - | 'date' - | 'date_period' - | 'time_duration' - | 'any' - | 'boolean' - | 'text' - | 'keyword' - | 'string' - | 'cartesian_point' - | 'cartesian_shape' - | 'geo_point' - | 'geo_shape' - | 'ip' - | 'version' - | 'void'; +/** + * This is the type of a parameter in a function definition. + */ +export type FunctionParameterType = Omit<SupportedDataType, 'unsupported'> | ArrayType | 'any'; + +/** + * This is the return type of a function definition. + */ +export type FunctionReturnType = Omit<SupportedDataType, 'unsupported'> | 'any' | 'void'; export interface FunctionDefinition { type: 'builtin' | 'agg' | 'eval'; @@ -138,6 +147,10 @@ export interface CommandBaseDefinition { name: string; alias?: string; description: string; + /** + * Whether to show or hide in autocomplete suggestion list + */ + hidden?: boolean; signature: { multipleParams: boolean; // innerType here is useful to drill down the type in case of "column" diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/esql_types.ts b/packages/kbn-esql-validation-autocomplete/src/shared/esql_types.ts index dab8769f8477a..fada6bea88134 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/esql_types.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/esql_types.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { ESQLDecimalLiteral, ESQLNumericLiteralType } from '@kbn/esql-ast/src/types'; +import { ESQLDecimalLiteral, ESQLLiteral, ESQLNumericLiteralType } from '@kbn/esql-ast/src/types'; +import { FunctionParameterType } from '../definitions/types'; export const ESQL_COMMON_NUMERIC_TYPES = ['double', 'long', 'integer'] as const; export const ESQL_NUMERIC_DECIMAL_TYPES = [ @@ -47,3 +48,29 @@ export function isNumericDecimalType(type: unknown): type is ESQLDecimalLiteral ESQL_NUMERIC_DECIMAL_TYPES.includes(type as (typeof ESQL_NUMERIC_DECIMAL_TYPES)[number]) ); } + +/** + * Compares two types, taking into account literal types + * @TODO strengthen typing here (remove `string`) + */ +export const compareTypesWithLiterals = ( + a: ESQLLiteral['literalType'] | FunctionParameterType | string, + b: ESQLLiteral['literalType'] | FunctionParameterType | string +) => { + if (a === b) { + return true; + } + if (a === 'decimal') { + return isNumericDecimalType(b); + } + if (b === 'decimal') { + return isNumericDecimalType(a); + } + if (a === 'string') { + return isStringType(b); + } + if (b === 'string') { + return isStringType(a); + } + return false; +}; diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts index e13326c2a9f43..0b9f704a43bcc 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts @@ -127,7 +127,7 @@ export function isComma(char: string) { } export function isSourceCommand({ label }: { label: string }) { - return ['FROM', 'ROW', 'SHOW'].includes(label); + return ['FROM', 'ROW', 'SHOW', 'METRICS'].includes(label); } let fnLookups: Map<string, FunctionDefinition> | undefined; @@ -277,7 +277,6 @@ const arrayToSingularMap: Map<FunctionParameterType, FunctionParameterType> = ne ['counter_integer[]', 'counter_integer'], ['counter_long[]', 'counter_long'], ['counter_double[]', 'counter_double'], - ['string[]', 'string'], ['keyword[]', 'keyword'], ['text[]', 'text'], ['datetime[]', 'date'], @@ -431,7 +430,7 @@ export function checkFunctionArgMatchesDefinition( return true; } if (arg.type === 'literal') { - const matched = compareLiteralType(argType, arg); + const matched = compareLiteralType(argType as string, arg); return matched; } if (arg.type === 'function') { @@ -457,7 +456,7 @@ export function checkFunctionArgMatchesDefinition( (ct) => ['any', 'null'].includes(ct) || argType === ct || - (ct === 'string' && ['text', 'keyword'].includes(argType)) + (ct === 'string' && ['text', 'keyword'].includes(argType as string)) ); } if (arg.type === 'inlineCast') { @@ -645,3 +644,8 @@ export const isParam = (x: unknown): x is ESQLParamLiteral => typeof x === 'object' && (x as ESQLParamLiteral).type === 'literal' && (x as ESQLParamLiteral).literalType === 'param'; + +/** + * Compares two strings in a case-insensitive manner + */ +export const noCaseCompare = (a: string, b: string) => a.toLowerCase() === b.toLowerCase(); diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts b/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts index ee1a912c688ea..16855a0498250 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts @@ -107,7 +107,7 @@ function addVariableFromAssignment( const rightHandSideArgType = getAssignRightHandSideType(assignOperation.args[1], fields); addToVariableOccurrencies(variables, { name: assignOperation.args[0].name, - type: rightHandSideArgType || 'double' /* fallback to number */, + type: (rightHandSideArgType as string) || 'double' /* fallback to number */, location: assignOperation.args[0].location, }); } @@ -156,9 +156,9 @@ export function collectVariables( ): Map<string, ESQLVariable[]> { const variables = new Map<string, ESQLVariable[]>(); for (const command of commands) { - if (['row', 'eval', 'stats', 'metrics'].includes(command.name)) { + if (['row', 'eval', 'stats', 'inlinestats', 'metrics'].includes(command.name)) { collectVariablesFromList(command.args, fields, queryString, variables); - if (command.name === 'stats') { + if (command.name === 'stats' || command.name === 'inlinestats') { const commandOptionsWithAssignment = command.args.filter( (arg) => isOptionItem(arg) && arg.name === 'by' ) as ESQLCommandOption[]; diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts new file mode 100644 index 0000000000000..08a22e83b8e88 --- /dev/null +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/test_suites/validation.command.inlinestats.ts @@ -0,0 +1,386 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 helpers from '../helpers'; + +export const validationStatsCommandTestSuite = (setup: helpers.Setup) => { + describe('validation', () => { + describe('command', () => { + describe('INLINESTATS <aggregates> [ BY <grouping> ]', () => { + test('no errors on correct usage', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS by textField', []); + await expectErrors( + `FROM index + | EVAL doubleField * 3.281 + | INLINESTATS avg_doubleField = AVG(\`doubleField * 3.281\`)`, + [] + ); + await expectErrors( + `FROM index | INLINESTATS AVG(doubleField) by round(doubleField) + 1 | EVAL \`round(doubleField) + 1\` / 2`, + [] + ); + }); + + test('errors on invalid command start', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS ', [ + 'At least one aggregation or grouping expression required in [INLINESTATS]', + ]); + }); + + describe('... <aggregates> ...', () => { + test('no errors on correct usage', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS avg(doubleField) by 1', []); + await expectErrors('from a_index | INLINESTATS count(`doubleField`)', []); + await expectErrors('from a_index | INLINESTATS count(*)', []); + await expectErrors('from a_index | INLINESTATS count()', []); + await expectErrors('from a_index | INLINESTATS var0 = count(*)', []); + await expectErrors('from a_index | INLINESTATS var0 = count()', []); + await expectErrors('from a_index | INLINESTATS var0 = avg(doubleField), count(*)', []); + await expectErrors(`from a_index | INLINESTATS sum(case(false, 0, 1))`, []); + await expectErrors(`from a_index | INLINESTATS var0 = sum( case(false, 0, 1))`, []); + + // "or" must accept "null" + await expectErrors('from a_index | INLINESTATS count(textField == "a" or null)', []); + }); + + test('sub-command can reference aggregated field', async () => { + const { expectErrors } = await setup(); + + for (const subCommand of ['keep', 'drop', 'eval']) { + await expectErrors( + 'from a_index | INLINESTATS count(`doubleField`) | ' + + subCommand + + ' `count(``doubleField``)` ', + [] + ); + } + }); + + test('errors on agg and non-agg mix', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + 'from a_index | INLINESTATS sum( doubleField ) + abs( doubleField ) ', + [ + 'Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [sum(doubleField)+abs(doubleField)]', + ] + ); + await expectErrors( + 'from a_index | INLINESTATS abs( doubleField + sum( doubleField )) ', + [ + 'Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [abs(doubleField+sum(doubleField))]', + ] + ); + }); + + test('errors on each aggregation field, which does not contain at least one agg function', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS doubleField + 1', [ + 'At least one aggregation function required in [INLINESTATS], found [doubleField+1]', + ]); + await expectErrors('from a_index | INLINESTATS doubleField + 1, textField', [ + 'At least one aggregation function required in [INLINESTATS], found [doubleField+1]', + 'Expected an aggregate function or group but got [textField] of type [FieldAttribute]', + ]); + await expectErrors( + 'from a_index | INLINESTATS doubleField + 1, doubleField + 2, count()', + [ + 'At least one aggregation function required in [INLINESTATS], found [doubleField+1]', + 'At least one aggregation function required in [INLINESTATS], found [doubleField+2]', + ] + ); + await expectErrors( + 'from a_index | INLINESTATS doubleField + 1, doubleField + count(), count()', + ['At least one aggregation function required in [INLINESTATS], found [doubleField+1]'] + ); + await expectErrors('from a_index | INLINESTATS 5 + doubleField + 1', [ + 'At least one aggregation function required in [INLINESTATS], found [5+doubleField+1]', + ]); + await expectErrors('from a_index | INLINESTATS doubleField + 1 by ipField', [ + 'At least one aggregation function required in [INLINESTATS], found [doubleField+1]', + ]); + }); + + test('errors when input is not an aggregate function', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS doubleField ', [ + 'Expected an aggregate function or group but got [doubleField] of type [FieldAttribute]', + ]); + }); + + test('various errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS doubleField=', [ + "SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + ]); + await expectErrors('from a_index | INLINESTATS doubleField=5 by ', [ + "SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + ]); + await expectErrors('from a_index | INLINESTATS avg(doubleField) by wrongField', [ + 'Unknown column [wrongField]', + ]); + await expectErrors('from a_index | INLINESTATS avg(doubleField) by wrongField + 1', [ + 'Unknown column [wrongField]', + ]); + await expectErrors( + 'from a_index | INLINESTATS avg(doubleField) by var0 = wrongField + 1', + ['Unknown column [wrongField]'] + ); + await expectErrors('from a_index | INLINESTATS var0 = avg(fn(number)), count(*)', [ + 'Unknown function [fn]', + ]); + }); + + test('semantic errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS count(round(*))', [ + 'Using wildcards (*) in round is not allowed', + ]); + await expectErrors('from a_index | INLINESTATS count(count(*))', [ + `Aggregate function's parameters must be an attribute, literal or a non-aggregation function; found [count(*)] of type [long]`, + ]); + }); + }); + + describe('... BY <grouping>', () => { + test('no errors on correct usage', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + 'from a_index | INLINESTATS avg(doubleField), percentile(doubleField, 50) by ipField', + [] + ); + await expectErrors( + 'from a_index | INLINESTATS avg(doubleField), percentile(doubleField, 50) BY ipField', + [] + ); + await expectErrors( + 'from a_index | INLINESTATS avg(doubleField), percentile(doubleField, 50) + 1 by ipField', + [] + ); + for (const op of ['+', '-', '*', '/', '%']) { + await expectErrors( + `from a_index | INLINESTATS avg(doubleField) ${op} percentile(doubleField, 50) BY ipField`, + [] + ); + } + }); + + test('cannot specify <grouping> without <aggregates>', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS by ', [ + "SyntaxError: mismatched input '<EOF>' expecting {QUOTED_STRING, INTEGER_LITERAL, DECIMAL_LITERAL, 'false', '(', 'not', 'null', '?', 'true', '+', '-', NAMED_OR_POSITIONAL_PARAM, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER}", + ]); + }); + + test('syntax errors in <aggregates>', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS count(* + 1) BY ipField', [ + "SyntaxError: no viable alternative at input 'count(* +'", + ]); + await expectErrors( + 'from a_index | INLINESTATS count(* + round(doubleField)) BY ipField', + ["SyntaxError: no viable alternative at input 'count(* +'"] + ); + }); + + test('semantic errors in <aggregates>', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from a_index | INLINESTATS count(round(*)) BY ipField', [ + 'Using wildcards (*) in round is not allowed', + ]); + await expectErrors('from a_index | INLINESTATS count(count(*)) BY ipField', [ + `Aggregate function's parameters must be an attribute, literal or a non-aggregation function; found [count(*)] of type [long]`, + ]); + }); + + test('various errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + 'from a_index | INLINESTATS avg(doubleField) by percentile(doubleField)', + ['INLINESTATS BY does not support function percentile'] + ); + await expectErrors( + 'from a_index | INLINESTATS avg(doubleField) by textField, percentile(doubleField) by ipField', + [ + "SyntaxError: mismatched input 'by' expecting <EOF>", + 'INLINESTATS BY does not support function percentile', + ] + ); + }); + + describe('constant-only parameters', () => { + test('no errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + 'from index | INLINESTATS by bucket(dateField, 1 + 30 / 10, "", "")', + [] + ); + await expectErrors( + 'from index | INLINESTATS by bucket(dateField, 1 + 30 / 10, concat("", ""), "")', + ['Argument of [bucket] must be [date], found value [concat("","")] type [keyword]'] + ); + }); + + test('errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors('from index | INLINESTATS by bucket(dateField, pi(), "", "")', [ + 'Argument of [bucket] must be [integer], found value [pi()] type [double]', + ]); + + await expectErrors( + 'from index | INLINESTATS by bucket(dateField, abs(doubleField), "", "")', + ['Argument of [bucket] must be a constant, received [abs(doubleField)]'] + ); + await expectErrors( + 'from index | INLINESTATS by bucket(dateField, abs(length(doubleField)), "", "")', + ['Argument of [bucket] must be a constant, received [abs(length(doubleField))]'] + ); + await expectErrors( + 'from index | INLINESTATS by bucket(dateField, doubleField, textField, textField)', + [ + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + }); + }); + }); + + describe('nesting', () => { + const NESTING_LEVELS = 4; + const NESTED_DEPTHS = Array(NESTING_LEVELS) + .fill(0) + .map((_, i) => i + 1); + + for (const nesting of NESTED_DEPTHS) { + describe(`depth = ${nesting}`, () => { + describe('builtin', () => { + const builtinWrapping = Array(nesting).fill('+1').join(''); + + test('no errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + `from a_index | INLINESTATS 5 + avg(doubleField) ${builtinWrapping}`, + [] + ); + await expectErrors( + `from a_index | INLINESTATS 5 ${builtinWrapping} + avg(doubleField)`, + [] + ); + }); + + test('errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + `from a_index | INLINESTATS 5 ${builtinWrapping} + doubleField`, + [ + `At least one aggregation function required in [INLINESTATS], found [5${builtinWrapping}+doubleField]`, + ] + ); + await expectErrors( + `from a_index | INLINESTATS 5 + doubleField ${builtinWrapping}`, + [ + `At least one aggregation function required in [INLINESTATS], found [5+doubleField${builtinWrapping}]`, + ] + ); + await expectErrors( + `from a_index | INLINESTATS 5 + doubleField ${builtinWrapping}, var0 = sum(doubleField)`, + [ + `At least one aggregation function required in [INLINESTATS], found [5+doubleField${builtinWrapping}]`, + ] + ); + }); + }); + + describe('EVAL', () => { + const evalWrapping = Array(nesting).fill('round(').join(''); + const closingWrapping = Array(nesting).fill(')').join(''); + + test('no errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + `from a_index | INLINESTATS ${evalWrapping} sum(doubleField) ${closingWrapping}`, + [] + ); + await expectErrors( + `from a_index | INLINESTATS ${evalWrapping} sum(doubleField) ${closingWrapping} + ${evalWrapping} sum(doubleField) ${closingWrapping}`, + [] + ); + await expectErrors( + `from a_index | INLINESTATS ${evalWrapping} sum(doubleField + doubleField) ${closingWrapping}`, + [] + ); + await expectErrors( + `from a_index | INLINESTATS ${evalWrapping} sum(doubleField + round(doubleField)) ${closingWrapping}`, + [] + ); + await expectErrors( + `from a_index | INLINESTATS ${evalWrapping} sum(doubleField + round(doubleField)) ${closingWrapping} + ${evalWrapping} sum(doubleField + round(doubleField)) ${closingWrapping}`, + [] + ); + await expectErrors( + `from a_index | INLINESTATS sum(${evalWrapping} doubleField ${closingWrapping} )`, + [] + ); + await expectErrors( + `from a_index | INLINESTATS sum(${evalWrapping} doubleField ${closingWrapping} ) + sum(${evalWrapping} doubleField ${closingWrapping} )`, + [] + ); + }); + + test('errors', async () => { + const { expectErrors } = await setup(); + + await expectErrors( + `from a_index | INLINESTATS ${evalWrapping} doubleField + sum(doubleField) ${closingWrapping}`, + [ + `Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [${evalWrapping}doubleField+sum(doubleField)${closingWrapping}]`, + ] + ); + await expectErrors( + `from a_index | INLINESTATS ${evalWrapping} doubleField + sum(doubleField) ${closingWrapping}, var0 = sum(doubleField)`, + [ + `Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [${evalWrapping}doubleField+sum(doubleField)${closingWrapping}]`, + ] + ); + await expectErrors( + `from a_index | INLINESTATS var0 = ${evalWrapping} doubleField + sum(doubleField) ${closingWrapping}, var1 = sum(doubleField)`, + [ + `Cannot combine aggregation and non-aggregation values in [INLINESTATS], found [${evalWrapping}doubleField+sum(doubleField)${closingWrapping}]`, + ] + ); + }); + }); + }); + } + }); + }); + }); + }); +}; diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.command.inlinestats.ts b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.command.inlinestats.ts new file mode 100644 index 0000000000000..cc4a33cb72b88 --- /dev/null +++ b/packages/kbn-esql-validation-autocomplete/src/validation/__tests__/validation.command.inlinestats.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import * as helpers from './helpers'; +import { validationStatsCommandTestSuite } from './test_suites/validation.command.stats'; + +validationStatsCommandTestSuite(helpers.setup); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index 0b601ab2f66a1..84f16cedf03b9 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -9,56 +9,44 @@ ], "fields": [ { - "name": "doubleField", - "type": "double" - }, - { - "name": "unsignedLongField", - "type": "unsigned_long" - }, - { - "name": "longField", - "type": "long" + "name": "booleanField", + "type": "boolean" }, { - "name": "integerField", - "type": "integer" + "name": "dateField", + "type": "date" }, { - "name": "counterIntegerField", - "type": "counter_integer" + "name": "doubleField", + "type": "double" }, { - "name": "counterLongField", - "type": "counter_long" + "name": "ipField", + "type": "ip" }, { - "name": "counterDoubleField", - "type": "counter_double" + "name": "keywordField", + "type": "keyword" }, { - "name": "dateField", - "type": "date" + "name": "integerField", + "type": "integer" }, { - "name": "datePeriodField", - "type": "date_period" + "name": "longField", + "type": "long" }, { "name": "textField", "type": "text" }, { - "name": "keywordField", - "type": "keyword" - }, - { - "name": "booleanField", - "type": "boolean" + "name": "unsignedLongField", + "type": "unsigned_long" }, { - "name": "ipField", - "type": "ip" + "name": "versionField", + "type": "version" }, { "name": "cartesianPointField", @@ -77,8 +65,16 @@ "type": "geo_shape" }, { - "name": "versionField", - "type": "version" + "name": "counterIntegerField", + "type": "counter_integer" + }, + { + "name": "counterLongField", + "type": "counter_long" + }, + { + "name": "counterDoubleField", + "type": "counter_double" }, { "name": "any#Char$Field", @@ -2323,13 +2319,6 @@ ], "warning": [] }, - { - "query": "from unsupported_index | keep unsupported_field", - "error": [], - "warning": [ - "Field [unsupported_field] cannot be retrieved, it is unsupported or not indexed; returning null" - ] - }, { "query": "FROM index | STATS ROUND(AVG(doubleField * 1.5)), COUNT(*), MIN(doubleField * 10) | KEEP `MIN(doubleField * 10)`", "error": [], @@ -3721,362 +3710,282 @@ "warning": [] }, { - "query": "from a_index | where doubleField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField Is nOt NuLL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField Is nOt NuLL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where longField IS NULL", + "query": "from a_index | where booleanField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS null", + "query": "from a_index | where booleanField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where longField is null", + "query": "from a_index | where booleanField is null", "error": [], "warning": [] }, { - "query": "from a_index | where longField is NULL", + "query": "from a_index | where booleanField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS NOT NULL", + "query": "from a_index | where booleanField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS NOT null", + "query": "from a_index | where booleanField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS not NULL", + "query": "from a_index | where booleanField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField Is nOt NuLL", + "query": "from a_index | where booleanField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS NULL", + "query": "from a_index | where dateField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS null", + "query": "from a_index | where dateField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where integerField is null", + "query": "from a_index | where dateField is null", "error": [], "warning": [] }, { - "query": "from a_index | where integerField is NULL", + "query": "from a_index | where dateField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS NOT NULL", + "query": "from a_index | where dateField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS NOT null", + "query": "from a_index | where dateField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS not NULL", + "query": "from a_index | where dateField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField Is nOt NuLL", + "query": "from a_index | where dateField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS NULL", + "query": "from a_index | where doubleField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS null", + "query": "from a_index | where doubleField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField is null", + "query": "from a_index | where doubleField is null", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField is NULL", + "query": "from a_index | where doubleField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS NOT NULL", + "query": "from a_index | where doubleField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS NOT null", + "query": "from a_index | where doubleField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS not NULL", + "query": "from a_index | where doubleField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField Is nOt NuLL", + "query": "from a_index | where doubleField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS NULL", + "query": "from a_index | where ipField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS null", + "query": "from a_index | where ipField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField is null", + "query": "from a_index | where ipField is null", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField is NULL", + "query": "from a_index | where ipField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS NOT NULL", + "query": "from a_index | where ipField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS NOT null", + "query": "from a_index | where ipField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS not NULL", + "query": "from a_index | where ipField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField Is nOt NuLL", + "query": "from a_index | where ipField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS NULL", + "query": "from a_index | where keywordField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS null", + "query": "from a_index | where keywordField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField is null", + "query": "from a_index | where keywordField is null", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField is NULL", + "query": "from a_index | where keywordField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS NOT NULL", + "query": "from a_index | where keywordField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS NOT null", + "query": "from a_index | where keywordField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS not NULL", + "query": "from a_index | where keywordField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField Is nOt NuLL", + "query": "from a_index | where keywordField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS NULL", + "query": "from a_index | where integerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS null", + "query": "from a_index | where integerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where dateField is null", + "query": "from a_index | where integerField is null", "error": [], "warning": [] }, { - "query": "from a_index | where dateField is NULL", + "query": "from a_index | where integerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS NOT NULL", + "query": "from a_index | where integerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS NOT null", + "query": "from a_index | where integerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS not NULL", + "query": "from a_index | where integerField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField Is nOt NuLL", + "query": "from a_index | where integerField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS NULL", + "query": "from a_index | where longField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS null", + "query": "from a_index | where longField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField is null", + "query": "from a_index | where longField is null", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField is NULL", + "query": "from a_index | where longField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS NOT NULL", + "query": "from a_index | where longField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS NOT null", + "query": "from a_index | where longField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS not NULL", + "query": "from a_index | where longField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField Is nOt NuLL", + "query": "from a_index | where longField Is nOt NuLL", "error": [], "warning": [] }, @@ -4121,122 +4030,82 @@ "warning": [] }, { - "query": "from a_index | where keywordField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField Is nOt NuLL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where booleanField IS NULL", + "query": "from a_index | where unsignedLongField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS null", + "query": "from a_index | where unsignedLongField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField is null", + "query": "from a_index | where unsignedLongField is null", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField is NULL", + "query": "from a_index | where unsignedLongField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS NOT NULL", + "query": "from a_index | where unsignedLongField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS NOT null", + "query": "from a_index | where unsignedLongField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS not NULL", + "query": "from a_index | where unsignedLongField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField Is nOt NuLL", + "query": "from a_index | where unsignedLongField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS NULL", + "query": "from a_index | where versionField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS null", + "query": "from a_index | where versionField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where ipField is null", + "query": "from a_index | where versionField is null", "error": [], "warning": [] }, { - "query": "from a_index | where ipField is NULL", + "query": "from a_index | where versionField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS NOT NULL", + "query": "from a_index | where versionField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS NOT null", + "query": "from a_index | where versionField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS not NULL", + "query": "from a_index | where versionField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField Is nOt NuLL", + "query": "from a_index | where versionField Is nOt NuLL", "error": [], "warning": [] }, @@ -4401,42 +4270,122 @@ "warning": [] }, { - "query": "from a_index | where versionField IS NULL", + "query": "from a_index | where counterIntegerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS null", + "query": "from a_index | where counterIntegerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where versionField is null", + "query": "from a_index | where counterIntegerField is null", "error": [], "warning": [] }, { - "query": "from a_index | where versionField is NULL", + "query": "from a_index | where counterIntegerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS NOT NULL", + "query": "from a_index | where counterIntegerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS NOT null", + "query": "from a_index | where counterIntegerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS not NULL", + "query": "from a_index | where counterIntegerField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField Is nOt NuLL", + "query": "from a_index | where counterIntegerField Is nOt NuLL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField Is nOt NuLL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField Is nOt NuLL", "error": [], "warning": [] }, @@ -4572,317 +4521,247 @@ "warning": [] }, { - "query": "from a_index | eval doubleField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval longField IS NULL", + "query": "from a_index | eval booleanField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS null", + "query": "from a_index | eval booleanField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval longField is null", + "query": "from a_index | eval booleanField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval longField is NULL", + "query": "from a_index | eval booleanField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS NOT NULL", + "query": "from a_index | eval booleanField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS NOT null", + "query": "from a_index | eval booleanField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS not NULL", + "query": "from a_index | eval booleanField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS NULL", + "query": "from a_index | eval dateField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS null", + "query": "from a_index | eval dateField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField is null", + "query": "from a_index | eval dateField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField is NULL", + "query": "from a_index | eval dateField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS NOT NULL", + "query": "from a_index | eval dateField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS NOT null", + "query": "from a_index | eval dateField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS not NULL", + "query": "from a_index | eval dateField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS NULL", + "query": "from a_index | eval doubleField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS null", + "query": "from a_index | eval doubleField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField is null", + "query": "from a_index | eval doubleField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField is NULL", + "query": "from a_index | eval doubleField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS NOT NULL", + "query": "from a_index | eval doubleField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS NOT null", + "query": "from a_index | eval doubleField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS not NULL", + "query": "from a_index | eval doubleField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS NULL", + "query": "from a_index | eval ipField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS null", + "query": "from a_index | eval ipField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField is null", + "query": "from a_index | eval ipField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField is NULL", + "query": "from a_index | eval ipField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS NOT NULL", + "query": "from a_index | eval ipField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS NOT null", + "query": "from a_index | eval ipField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS not NULL", + "query": "from a_index | eval ipField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS NULL", + "query": "from a_index | eval keywordField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS null", + "query": "from a_index | eval keywordField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField is null", + "query": "from a_index | eval keywordField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField is NULL", + "query": "from a_index | eval keywordField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS NOT NULL", + "query": "from a_index | eval keywordField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS NOT null", + "query": "from a_index | eval keywordField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS not NULL", + "query": "from a_index | eval keywordField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS NULL", + "query": "from a_index | eval integerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS null", + "query": "from a_index | eval integerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField is null", + "query": "from a_index | eval integerField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField is NULL", + "query": "from a_index | eval integerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS NOT NULL", + "query": "from a_index | eval integerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS NOT null", + "query": "from a_index | eval integerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS not NULL", + "query": "from a_index | eval integerField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS NULL", + "query": "from a_index | eval longField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS null", + "query": "from a_index | eval longField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField is null", + "query": "from a_index | eval longField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField is NULL", + "query": "from a_index | eval longField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS NOT NULL", + "query": "from a_index | eval longField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS NOT null", + "query": "from a_index | eval longField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS not NULL", + "query": "from a_index | eval longField IS not NULL", "error": [], "warning": [] }, @@ -4922,107 +4801,72 @@ "warning": [] }, { - "query": "from a_index | eval keywordField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval booleanField IS NULL", + "query": "from a_index | eval unsignedLongField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS null", + "query": "from a_index | eval unsignedLongField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField is null", + "query": "from a_index | eval unsignedLongField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField is NULL", + "query": "from a_index | eval unsignedLongField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS NOT NULL", + "query": "from a_index | eval unsignedLongField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS NOT null", + "query": "from a_index | eval unsignedLongField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS not NULL", + "query": "from a_index | eval unsignedLongField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS NULL", + "query": "from a_index | eval versionField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS null", + "query": "from a_index | eval versionField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField is null", + "query": "from a_index | eval versionField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField is NULL", + "query": "from a_index | eval versionField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS NOT NULL", + "query": "from a_index | eval versionField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS NOT null", + "query": "from a_index | eval versionField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS not NULL", + "query": "from a_index | eval versionField IS not NULL", "error": [], "warning": [] }, @@ -5167,37 +5011,107 @@ "warning": [] }, { - "query": "from a_index | eval versionField IS NULL", + "query": "from a_index | eval counterIntegerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS null", + "query": "from a_index | eval counterIntegerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField is null", + "query": "from a_index | eval counterIntegerField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField is NULL", + "query": "from a_index | eval counterIntegerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS NOT NULL", + "query": "from a_index | eval counterIntegerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS NOT null", + "query": "from a_index | eval counterIntegerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS not NULL", + "query": "from a_index | eval counterIntegerField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS not NULL", "error": [], "warning": [] }, @@ -11623,6 +11537,46 @@ "error": [], "warning": [] }, + { + "query": "row var = coalesce(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row coalesce(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = coalesce(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = coalesce(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = coalesce(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row coalesce(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = coalesce(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = coalesce(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = concat(\"a\", \"a\")", "error": [], @@ -12101,6 +12055,11 @@ ], "warning": [] }, + { + "query": "from a_index | eval var = date_diff(to_string(booleanField), dateField, dateField)", + "error": [], + "warning": [] + }, { "query": "row var = date_extract(\"ALIGNED_DAY_OF_WEEK_IN_MONTH\", to_datetime(\"2021-01-01T00:00:00Z\"))", "error": [], @@ -12213,6 +12172,11 @@ ], "warning": [] }, + { + "query": "from a_index | eval var = date_extract(to_string(booleanField), dateField)", + "error": [], + "warning": [] + }, { "query": "row var = date_format(\"a\", to_datetime(\"2021-01-01T00:00:00Z\"))", "error": [], @@ -12310,6 +12274,11 @@ ], "warning": [] }, + { + "query": "from a_index | eval var = date_format(to_string(booleanField), dateField)", + "error": [], + "warning": [] + }, { "query": "row var = date_parse(\"a\", \"a\")", "error": [], @@ -12653,6 +12622,26 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval var = ends_with(keywordField, textField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval ends_with(keywordField, textField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = ends_with(textField, keywordField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval ends_with(textField, keywordField)", + "error": [], + "warning": [] + }, { "query": "row var = exp(5.5)", "error": [], @@ -13416,6 +13405,14 @@ "error": [], "warning": [] }, + { + "query": "from a_index | where greatest(cartesianPointField, cartesianPointField) > 0", + "error": [ + "Argument of [greatest] must be [boolean], found value [cartesianPointField] type [cartesian_point]", + "Argument of [greatest] must be [boolean], found value [cartesianPointField] type [cartesian_point]" + ], + "warning": [] + }, { "query": "row var = ip_prefix(to_ip(\"127.0.0.1\"), 5, 5)", "error": [], @@ -15420,6 +15417,46 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_append(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_append(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_append(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_append(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_append(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_append(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_append(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_append(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = mv_avg(5.5)", "error": [], @@ -16167,6 +16204,46 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_count(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_count(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_count(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_count(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_count(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_count(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_count(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_count(to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = mv_dedupe(true)", "error": [], @@ -16601,6 +16678,46 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_dedupe(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_dedupe(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_dedupe(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_dedupe(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_dedupe(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_dedupe(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_dedupe(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_dedupe(to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = mv_first(true)", "error": [], @@ -17050,6 +17167,46 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_first(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_first(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_first(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_first(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_first(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_first(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_first(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_first(to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = mv_last(true)", "error": [], @@ -17499,6 +17656,46 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_last(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_last(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_last(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_last(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_last(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row mv_last(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_last(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_last(to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = mv_max(true)", "error": [], @@ -18738,6 +18935,56 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_slice(to_cartesianpoint(\"POINT (30 10)\"), 5, 5)", + "error": [], + "warning": [] + }, + { + "query": "row mv_slice(to_cartesianpoint(\"POINT (30 10)\"), 5, 5)", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_slice(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_integer(true), to_integer(true))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_slice(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_integer(true), to_integer(true))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_slice(to_datetime(\"2021-01-01T00:00:00Z\"), to_integer(true), to_integer(true))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_slice(to_geopoint(\"POINT (30 10)\"), 5, 5)", + "error": [], + "warning": [] + }, + { + "query": "row mv_slice(to_geopoint(\"POINT (30 10)\"), 5, 5)", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_slice(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_integer(true), to_integer(true))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_slice(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_integer(true), to_integer(true))", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = mv_slice(dateField, to_integer(booleanField), to_integer(booleanField))", + "error": [], + "warning": [] + }, { "query": "row var = mv_sort(true, \"asc\")", "error": [], @@ -18938,6 +19185,78 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_sort(5, \"a\")", + "error": [], + "warning": [ + "Invalid option [\"a\"] for mv_sort. Supported options: [\"asc\", \"desc\"]." + ] + }, + { + "query": "row mv_sort(5, \"a\")", + "error": [], + "warning": [ + "Invalid option [\"a\"] for mv_sort. Supported options: [\"asc\", \"desc\"]." + ] + }, + { + "query": "row var = mv_sort(\"a\", \"a\")", + "error": [], + "warning": [ + "Invalid option [\"a\"] for mv_sort. Supported options: [\"asc\", \"desc\"]." + ] + }, + { + "query": "row mv_sort(\"a\", \"a\")", + "error": [], + "warning": [ + "Invalid option [\"a\"] for mv_sort. Supported options: [\"asc\", \"desc\"]." + ] + }, + { + "query": "row var = mv_sort(to_version(\"1.0.0\"), \"a\")", + "error": [], + "warning": [ + "Invalid option [\"a\"] for mv_sort. Supported options: [\"asc\", \"desc\"]." + ] + }, + { + "query": "row mv_sort(to_version(\"1.0.0\"), \"a\")", + "error": [], + "warning": [ + "Invalid option [\"a\"] for mv_sort. Supported options: [\"asc\", \"desc\"]." + ] + }, + { + "query": "from a_index | eval var = mv_sort(longField, keywordField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_sort(longField, keywordField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = mv_sort(textField, keywordField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_sort(textField, keywordField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = mv_sort(versionField, keywordField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_sort(versionField, keywordField)", + "error": [], + "warning": [] + }, { "query": "row var = mv_sum(5.5)", "error": [], @@ -21470,6 +21789,106 @@ "error": [], "warning": [] }, + { + "query": "row var = st_contains(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_contains(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_contains(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_contains(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_contains(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_contains(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_contains(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_contains(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = st_disjoint(cartesianPointField, cartesianPointField)", "error": [ @@ -21800,6 +22219,106 @@ "error": [], "warning": [] }, + { + "query": "row var = st_disjoint(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_disjoint(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_disjoint(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_disjoint(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_disjoint(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_disjoint(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_disjoint(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_disjoint(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = st_distance(cartesianPointField, cartesianPointField)", "error": [ @@ -21916,6 +22435,36 @@ "error": [], "warning": [] }, + { + "query": "row var = st_distance(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_distance(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_distance(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_distance(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_distance(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_distance(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = st_intersects(cartesianPointField, cartesianPointField)", "error": [ @@ -22246,6 +22795,106 @@ "error": [], "warning": [] }, + { + "query": "row var = st_intersects(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_intersects(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_intersects(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_intersects(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_intersects(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_intersects(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_intersects(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_intersects(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = st_within(cartesianPointField, cartesianPointField)", "error": [ @@ -22576,6 +23225,106 @@ "error": [], "warning": [] }, + { + "query": "row var = st_within(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_within(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_within(to_cartesianpoint(\"POINT (30 10)\"), to_cartesianshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_within(to_cartesianshape(\"POINT (30 10)\"), to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")), to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_within(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_within(to_geopoint(\"POINT (30 10)\"), to_geoshape(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_geopoint(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_within(to_geoshape(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_within(to_geoshape(to_geopoint(\"POINT (30 10)\")), to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = st_x(cartesianPointField)", "error": [ @@ -22691,6 +23440,36 @@ "error": [], "warning": [] }, + { + "query": "row var = st_x(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_x(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_x(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_x(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_x(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_x(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = st_y(cartesianPointField)", "error": [ @@ -22806,6 +23585,36 @@ "error": [], "warning": [] }, + { + "query": "row var = st_y(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_y(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_y(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_y(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row st_y(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = st_y(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = starts_with(\"a\", \"a\")", "error": [], @@ -22884,6 +23693,26 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval var = starts_with(keywordField, textField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval starts_with(keywordField, textField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = starts_with(textField, keywordField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval starts_with(textField, keywordField)", + "error": [], + "warning": [] + }, { "query": "row var = substring(\"a\", 5, 5)", "error": [], @@ -23756,6 +24585,21 @@ "error": [], "warning": [] }, + { + "query": "row var = to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_cartesianpoint(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = to_cartesianshape(cartesianPointField)", "error": [ @@ -23907,6 +24751,26 @@ "error": [], "warning": [] }, + { + "query": "row var = to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_cartesianshape(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_cartesianshape(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = to_datetime(to_datetime(\"2021-01-01T00:00:00Z\"))", "error": [], @@ -24839,6 +25703,21 @@ "error": [], "warning": [] }, + { + "query": "row var = to_geopoint(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row to_geopoint(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_geopoint(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = to_geoshape(geoPointField)", "error": [ @@ -24990,6 +25869,26 @@ "error": [], "warning": [] }, + { + "query": "row var = to_geoshape(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row to_geoshape(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_geoshape(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_geoshape(to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, { "query": "row var = to_integer(true)", "error": [], @@ -30576,16 +31475,6 @@ "error": [], "warning": [] }, - { - "query": "from a_index | stats var = max(datePeriodField)", - "error": [], - "warning": [] - }, - { - "query": "from a_index | stats max(datePeriodField)", - "error": [], - "warning": [] - }, { "query": "from a_index | stats var = max(booleanField)", "error": [], @@ -30669,20 +31558,6 @@ ], "warning": [] }, - { - "query": "from a_index | where max(datePeriodField)", - "error": [ - "WHERE does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | where max(datePeriodField) > 0", - "error": [ - "WHERE does not support function max" - ], - "warning": [] - }, { "query": "from a_index | where max(booleanField)", "error": [ @@ -30823,34 +31698,6 @@ ], "warning": [] }, - { - "query": "from a_index | eval var = max(datePeriodField)", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | eval var = max(datePeriodField) > 0", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | eval max(datePeriodField)", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | eval max(datePeriodField) > 0", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, { "query": "from a_index | eval var = max(booleanField)", "error": [ @@ -31212,16 +32059,6 @@ "error": [], "warning": [] }, - { - "query": "from a_index | stats var = min(datePeriodField)", - "error": [], - "warning": [] - }, - { - "query": "from a_index | stats min(datePeriodField)", - "error": [], - "warning": [] - }, { "query": "from a_index | stats var = min(booleanField)", "error": [], @@ -31305,20 +32142,6 @@ ], "warning": [] }, - { - "query": "from a_index | where min(datePeriodField)", - "error": [ - "WHERE does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | where min(datePeriodField) > 0", - "error": [ - "WHERE does not support function min" - ], - "warning": [] - }, { "query": "from a_index | where min(booleanField)", "error": [ @@ -31459,34 +32282,6 @@ ], "warning": [] }, - { - "query": "from a_index | eval var = min(datePeriodField)", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | eval var = min(datePeriodField) > 0", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | eval min(datePeriodField)", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | eval min(datePeriodField) > 0", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, { "query": "from a_index | eval var = min(booleanField)", "error": [ @@ -31664,6 +32459,85 @@ "error": [], "warning": [] }, + { + "query": "from a_index | stats var = count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | stats count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | stats var = round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField))", + "error": [], + "warning": [] + }, + { + "query": "from a_index | stats round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField))", + "error": [], + "warning": [] + }, + { + "query": "from a_index | stats var = round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)) + count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | stats round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)) + count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | sort count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [ + "SORT does not support function count_distinct" + ], + "warning": [] + }, + { + "query": "from a_index | where count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [ + "WHERE does not support function count_distinct" + ], + "warning": [] + }, + { + "query": "from a_index | where count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField) > 0", + "error": [ + "WHERE does not support function count_distinct" + ], + "warning": [] + }, + { + "query": "from a_index | eval var = count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [ + "EVAL does not support function count_distinct" + ], + "warning": [] + }, + { + "query": "from a_index | eval var = count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField) > 0", + "error": [ + "EVAL does not support function count_distinct" + ], + "warning": [] + }, + { + "query": "from a_index | eval count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)", + "error": [ + "EVAL does not support function count_distinct" + ], + "warning": [] + }, + { + "query": "from a_index | eval count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField) > 0", + "error": [ + "EVAL does not support function count_distinct" + ], + "warning": [] + }, { "query": "from a_index | stats var = st_centroid_agg(cartesianPointField)", "error": [], @@ -33395,6 +34269,530 @@ ], "warning": [] }, + { + "query": "from a_index | stats by bucket(dateField, integerField, now(), now())", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(dateField, integerField, now(), now())", + "error": [ + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, doubleField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, doubleField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, doubleField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, doubleField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, doubleField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, doubleField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, integerField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, integerField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, integerField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, integerField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, integerField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, integerField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, longField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, longField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, longField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, longField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(doubleField, integerField, longField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(doubleField, integerField, longField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, doubleField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, doubleField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, doubleField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, doubleField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, doubleField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, doubleField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, integerField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, integerField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, integerField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, integerField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, longField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, longField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, longField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, longField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(integerField, integerField, longField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(integerField, integerField, longField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, doubleField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, doubleField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, doubleField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, doubleField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, doubleField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, doubleField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, integerField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, integerField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, integerField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, integerField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, integerField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, integerField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, longField, doubleField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, longField, doubleField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [doubleField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, longField, integerField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, longField, integerField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [integerField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bucket(longField, integerField, longField, longField)", + "error": [ + "Argument of [bucket] must be a constant, received [integerField]", + "Argument of [bucket] must be a constant, received [longField]", + "Argument of [bucket] must be a constant, received [longField]" + ], + "warning": [] + }, + { + "query": "from a_index | stats by bin(longField, integerField, longField, longField)", + "error": [ + "Argument of [bin] must be a constant, received [integerField]", + "Argument of [bin] must be a constant, received [longField]", + "Argument of [bin] must be a constant, received [longField]" + ], + "warning": [] + }, { "query": "from a_index | stats var = percentile(doubleField, doubleField)", "error": [ @@ -35406,6 +36804,137 @@ "error": [], "warning": [] }, + { + "query": "row var = to_string(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row to_string(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_str(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_string(to_cartesianpoint(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_string(to_cartesianshape(to_cartesianpoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_string(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row to_string(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_str(to_geopoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_string(to_geopoint(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = to_string(to_geoshape(to_geopoint(\"POINT (30 10)\")))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_pseries_weighted_sum(5.5, 5.5)", + "error": [], + "warning": [] + }, + { + "query": "row mv_pseries_weighted_sum(5.5, 5.5)", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_pseries_weighted_sum(to_double(true), to_double(true))", + "error": [], + "warning": [] + }, + { + "query": "row var = mv_pseries_weighted_sum(true, true)", + "error": [ + "Argument of [mv_pseries_weighted_sum] must be [double], found value [true] type [boolean]", + "Argument of [mv_pseries_weighted_sum] must be [double], found value [true] type [boolean]" + ], + "warning": [] + }, + { + "query": "from a_index | where mv_pseries_weighted_sum(doubleField, doubleField) > 0", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where mv_pseries_weighted_sum(booleanField, booleanField) > 0", + "error": [ + "Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]", + "Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]" + ], + "warning": [] + }, + { + "query": "from a_index | eval var = mv_pseries_weighted_sum(doubleField, doubleField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_pseries_weighted_sum(doubleField, doubleField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = mv_pseries_weighted_sum(to_double(booleanField), to_double(booleanField))", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_pseries_weighted_sum(booleanField, booleanField)", + "error": [ + "Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]", + "Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]" + ], + "warning": [] + }, + { + "query": "from a_index | eval mv_pseries_weighted_sum(doubleField, doubleField, extraArg)", + "error": [ + "Error: [mv_pseries_weighted_sum] function expects exactly 2 arguments, got 3." + ], + "warning": [] + }, + { + "query": "from a_index | sort mv_pseries_weighted_sum(doubleField, doubleField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_pseries_weighted_sum(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_pseries_weighted_sum(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "f", "error": [ diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/types.ts b/packages/kbn-esql-validation-autocomplete/src/validation/types.ts index 41a0c9b2dfd6f..f6654540bcf86 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/types.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/types.ts @@ -7,6 +7,7 @@ */ import type { ESQLMessage, ESQLLocation } from '@kbn/esql-ast'; +import { FieldType } from '../definitions/types'; import type { EditorError } from '../types'; export interface ESQLVariable { @@ -17,10 +18,9 @@ export interface ESQLVariable { export interface ESQLRealField { name: string; - type: string; + type: FieldType; metadata?: { description?: string; - type?: string; }; } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index f4c75ae1e63a0..5e61c2631d9e1 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -11,7 +11,13 @@ import { writeFile, readFile } from 'fs/promises'; import { ignoreErrorsMap, validateQuery } from './validation'; import { evalFunctionDefinitions } from '../definitions/functions'; import { getFunctionSignatures } from '../definitions/helpers'; -import { FunctionDefinition, SupportedFieldType, supportedFieldTypes } from '../definitions/types'; +import { + FieldType, + FunctionDefinition, + SupportedDataType, + dataTypes, + fieldTypes as _fieldTypes, +} from '../definitions/types'; import { timeUnits, timeUnitsToSuggest } from '../definitions/literals'; import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; import capitalize from 'lodash/capitalize'; @@ -30,6 +36,8 @@ import { import { validationFromCommandTestSuite as runFromTestSuite } from './__tests__/test_suites/validation.command.from'; import { Setup, setup } from './__tests__/helpers'; +const fieldTypes = _fieldTypes.filter((type) => type !== 'unsupported'); + const NESTING_LEVELS = 4; const NESTED_DEPTHS = Array(NESTING_LEVELS) .fill(0) @@ -76,7 +84,7 @@ function getLiteralType(typeString: 'time_literal') { return `1 ${literals[typeString]}`; } -export const fieldNameFromType = (type: SupportedFieldType) => `${camelCase(type)}Field`; +export const fieldNameFromType = (type: FieldType) => `${camelCase(type)}Field`; function getFieldName( typeString: string, @@ -127,8 +135,8 @@ function getFieldMapping( date: 'now()', }; return params.map(({ name: _name, type, constantOnly, literalOptions, ...rest }) => { - const typeString: string = type; - if (supportedFieldTypes.includes(typeString as SupportedFieldType)) { + const typeString: string = type as string; + if (dataTypes.includes(typeString as SupportedDataType)) { if (useLiterals && literalOptions) { return { name: `"${literalOptions[0]}"`, @@ -541,13 +549,6 @@ describe('validation logic', () => { 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', - ] - ); testErrorsAndWarnings( `FROM index | STATS ROUND(AVG(doubleField * 1.5)), COUNT(*), MIN(doubleField * 10) | KEEP \`MIN(doubleField * 10)\``, @@ -874,7 +875,7 @@ describe('validation logic', () => { [] ); - for (const field of supportedFieldTypes) { + for (const field of fieldTypes) { testErrorsAndWarnings(`from a_index | where ${fieldNameFromType(field)} IS NULL`, []); testErrorsAndWarnings(`from a_index | where ${fieldNameFromType(field)} IS null`, []); testErrorsAndWarnings(`from a_index | where ${fieldNameFromType(field)} is null`, []); @@ -937,7 +938,7 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval a=["a", "b"]', []); testErrorsAndWarnings('from a_index | eval a=null', []); - for (const field of supportedFieldTypes) { + for (const field of fieldTypes) { testErrorsAndWarnings(`from a_index | eval ${fieldNameFromType(field)} IS NULL`, []); testErrorsAndWarnings(`from a_index | eval ${fieldNameFromType(field)} IS null`, []); testErrorsAndWarnings(`from a_index | eval ${fieldNameFromType(field)} is null`, []); @@ -2463,6 +2464,46 @@ describe('validation logic', () => { 'from a_index | eval coalesce(concat("20", "22"), concat("20", "22"))', [] ); + + testErrorsAndWarnings( + 'row var = coalesce(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row coalesce(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = coalesce(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = coalesce(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = coalesce(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row coalesce(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = coalesce(to_geopoint(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = coalesce(to_geoshape(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); }); describe('concat', () => { @@ -2661,6 +2702,11 @@ describe('validation logic', () => { 'Argument of [date_diff] must be [date], found value [concat("20","22")] type [keyword]', ] ); + + testErrorsAndWarnings( + 'from a_index | eval var = date_diff(to_string(booleanField), dateField, dateField)', + [] + ); }); describe('date_extract', () => { @@ -2742,6 +2788,11 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval date_extract(textField, concat("20", "22"))', [ 'Argument of [date_extract] must be [date], found value [concat("20","22")] type [keyword]', ]); + + testErrorsAndWarnings( + 'from a_index | eval var = date_extract(to_string(booleanField), dateField)', + [] + ); }); describe('date_format', () => { @@ -2790,6 +2841,11 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval date_format(textField, concat("20", "22"))', [ 'Argument of [date_format] must be [date], found value [concat("20","22")] type [keyword]', ]); + + testErrorsAndWarnings( + 'from a_index | eval var = date_format(to_string(booleanField), dateField)', + [] + ); }); describe('date_parse', () => { @@ -2950,6 +3006,10 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort ends_with(keywordField, keywordField)', []); testErrorsAndWarnings('from a_index | eval ends_with(null, null)', []); testErrorsAndWarnings('row nullVar = null | eval ends_with(nullVar, nullVar)', []); + testErrorsAndWarnings('from a_index | eval var = ends_with(keywordField, textField)', []); + testErrorsAndWarnings('from a_index | eval ends_with(keywordField, textField)', []); + testErrorsAndWarnings('from a_index | eval var = ends_with(textField, keywordField)', []); + testErrorsAndWarnings('from a_index | eval ends_with(textField, keywordField)', []); }); describe('exp', () => { @@ -3217,6 +3277,14 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort greatest(booleanField)', []); testErrorsAndWarnings('from a_index | eval greatest(null)', []); testErrorsAndWarnings('row nullVar = null | eval greatest(nullVar)', []); + + testErrorsAndWarnings( + 'from a_index | where greatest(cartesianPointField, cartesianPointField) > 0', + [ + 'Argument of [greatest] must be [boolean], found value [cartesianPointField] type [cartesian_point]', + 'Argument of [greatest] must be [boolean], found value [cartesianPointField] type [cartesian_point]', + ] + ); }); describe('ip_prefix', () => { @@ -4108,6 +4176,46 @@ describe('validation logic', () => { 'from a_index | eval mv_append(concat("20", "22"), concat("20", "22"))', [] ); + + testErrorsAndWarnings( + 'row var = mv_append(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row mv_append(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_append(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_append(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_append(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row mv_append(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_append(to_geopoint(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_append(to_geoshape(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); }); describe('mv_avg', () => { @@ -4344,6 +4452,23 @@ describe('validation logic', () => { testErrorsAndWarnings('row nullVar = null | eval mv_count(nullVar)', []); testErrorsAndWarnings('from a_index | eval mv_count("2022")', []); testErrorsAndWarnings('from a_index | eval mv_count(concat("20", "22"))', []); + testErrorsAndWarnings('row var = mv_count(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_count(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = mv_count(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_count(to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings('row var = mv_count(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_count(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = mv_count(to_geopoint(to_geopoint("POINT (30 10)")))', []); + testErrorsAndWarnings('row var = mv_count(to_geoshape(to_geopoint("POINT (30 10)")))', []); }); describe('mv_dedupe', () => { @@ -4480,6 +4605,23 @@ describe('validation logic', () => { testErrorsAndWarnings('row nullVar = null | eval mv_dedupe(nullVar)', []); testErrorsAndWarnings('from a_index | eval mv_dedupe("2022")', []); testErrorsAndWarnings('from a_index | eval mv_dedupe(concat("20", "22"))', []); + testErrorsAndWarnings('row var = mv_dedupe(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_dedupe(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = mv_dedupe(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_dedupe(to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings('row var = mv_dedupe(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_dedupe(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = mv_dedupe(to_geopoint(to_geopoint("POINT (30 10)")))', []); + testErrorsAndWarnings('row var = mv_dedupe(to_geoshape(to_geopoint("POINT (30 10)")))', []); }); describe('mv_first', () => { @@ -4613,6 +4755,23 @@ describe('validation logic', () => { testErrorsAndWarnings('row nullVar = null | eval mv_first(nullVar)', []); testErrorsAndWarnings('from a_index | eval mv_first("2022")', []); testErrorsAndWarnings('from a_index | eval mv_first(concat("20", "22"))', []); + testErrorsAndWarnings('row var = mv_first(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_first(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = mv_first(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_first(to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings('row var = mv_first(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_first(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = mv_first(to_geopoint(to_geopoint("POINT (30 10)")))', []); + testErrorsAndWarnings('row var = mv_first(to_geoshape(to_geopoint("POINT (30 10)")))', []); }); describe('mv_last', () => { @@ -4746,6 +4905,23 @@ describe('validation logic', () => { testErrorsAndWarnings('row nullVar = null | eval mv_last(nullVar)', []); testErrorsAndWarnings('from a_index | eval mv_last("2022")', []); testErrorsAndWarnings('from a_index | eval mv_last(concat("20", "22"))', []); + testErrorsAndWarnings('row var = mv_last(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_last(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = mv_last(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_last(to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings('row var = mv_last(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row mv_last(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = mv_last(to_geopoint(to_geopoint("POINT (30 10)")))', []); + testErrorsAndWarnings('row var = mv_last(to_geoshape(to_geopoint("POINT (30 10)")))', []); }); describe('mv_max', () => { @@ -5319,6 +5495,41 @@ describe('validation logic', () => { 'from a_index | eval mv_slice(concat("20", "22"), integerField, integerField)', [] ); + testErrorsAndWarnings('row var = mv_slice(to_cartesianpoint("POINT (30 10)"), 5, 5)', []); + testErrorsAndWarnings('row mv_slice(to_cartesianpoint("POINT (30 10)"), 5, 5)', []); + + testErrorsAndWarnings( + 'row var = mv_slice(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_integer(true), to_integer(true))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_slice(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_integer(true), to_integer(true))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_slice(to_datetime("2021-01-01T00:00:00Z"), to_integer(true), to_integer(true))', + [] + ); + + testErrorsAndWarnings('row var = mv_slice(to_geopoint("POINT (30 10)"), 5, 5)', []); + testErrorsAndWarnings('row mv_slice(to_geopoint("POINT (30 10)"), 5, 5)', []); + + testErrorsAndWarnings( + 'row var = mv_slice(to_geopoint(to_geopoint("POINT (30 10)")), to_integer(true), to_integer(true))', + [] + ); + + testErrorsAndWarnings( + 'row var = mv_slice(to_geoshape(to_geopoint("POINT (30 10)")), to_integer(true), to_integer(true))', + [] + ); + + testErrorsAndWarnings( + 'from a_index | eval var = mv_slice(dateField, to_integer(booleanField), to_integer(booleanField))', + [] + ); }); describe('mv_sort', () => { @@ -5370,6 +5581,42 @@ describe('validation logic', () => { testErrorsAndWarnings('row nullVar = null | eval mv_sort(nullVar, nullVar)', []); testErrorsAndWarnings('from a_index | eval mv_sort("2022", "asc")', []); testErrorsAndWarnings('from a_index | eval mv_sort(concat("20", "22"), "asc")', []); + testErrorsAndWarnings( + 'row var = mv_sort(5, "a")', + [], + ['Invalid option ["a"] for mv_sort. Supported options: ["asc", "desc"].'] + ); + testErrorsAndWarnings( + 'row mv_sort(5, "a")', + [], + ['Invalid option ["a"] for mv_sort. Supported options: ["asc", "desc"].'] + ); + testErrorsAndWarnings( + 'row var = mv_sort("a", "a")', + [], + ['Invalid option ["a"] for mv_sort. Supported options: ["asc", "desc"].'] + ); + testErrorsAndWarnings( + 'row mv_sort("a", "a")', + [], + ['Invalid option ["a"] for mv_sort. Supported options: ["asc", "desc"].'] + ); + testErrorsAndWarnings( + 'row var = mv_sort(to_version("1.0.0"), "a")', + [], + ['Invalid option ["a"] for mv_sort. Supported options: ["asc", "desc"].'] + ); + testErrorsAndWarnings( + 'row mv_sort(to_version("1.0.0"), "a")', + [], + ['Invalid option ["a"] for mv_sort. Supported options: ["asc", "desc"].'] + ); + testErrorsAndWarnings('from a_index | eval var = mv_sort(longField, keywordField)', []); + testErrorsAndWarnings('from a_index | eval mv_sort(longField, keywordField)', []); + testErrorsAndWarnings('from a_index | eval var = mv_sort(textField, keywordField)', []); + testErrorsAndWarnings('from a_index | eval mv_sort(textField, keywordField)', []); + testErrorsAndWarnings('from a_index | eval var = mv_sort(versionField, keywordField)', []); + testErrorsAndWarnings('from a_index | eval mv_sort(versionField, keywordField)', []); }); describe('mv_sum', () => { @@ -6522,6 +6769,106 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval st_contains(null, null)', []); testErrorsAndWarnings('row nullVar = null | eval st_contains(nullVar, nullVar)', []); + + testErrorsAndWarnings( + 'row var = st_contains(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_contains(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_contains(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_contains(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_contains(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_geopoint(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_contains(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_geopoint(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_contains(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_geoshape(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_contains(to_geoshape(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); }); describe('st_disjoint', () => { @@ -6760,15 +7107,115 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval st_disjoint(null, null)', []); testErrorsAndWarnings('row nullVar = null | eval st_disjoint(nullVar, nullVar)', []); - }); - describe('st_distance', () => { - testErrorsAndWarnings('row var = st_distance(cartesianPointField, cartesianPointField)', [ - 'Unknown column [cartesianPointField]', - 'Unknown column [cartesianPointField]', - ]); - testErrorsAndWarnings('row st_distance(cartesianPointField, cartesianPointField)', [ - 'Unknown column [cartesianPointField]', + testErrorsAndWarnings( + 'row var = st_disjoint(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_disjoint(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_disjoint(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_disjoint(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_disjoint(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_geopoint(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_disjoint(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_geopoint(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_disjoint(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_geoshape(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_disjoint(to_geoshape(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); + }); + + describe('st_distance', () => { + testErrorsAndWarnings('row var = st_distance(cartesianPointField, cartesianPointField)', [ + 'Unknown column [cartesianPointField]', + 'Unknown column [cartesianPointField]', + ]); + testErrorsAndWarnings('row st_distance(cartesianPointField, cartesianPointField)', [ + 'Unknown column [cartesianPointField]', 'Unknown column [cartesianPointField]', ]); @@ -6838,6 +7285,36 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval st_distance(null, null)', []); testErrorsAndWarnings('row nullVar = null | eval st_distance(nullVar, nullVar)', []); + + testErrorsAndWarnings( + 'row var = st_distance(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_distance(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_distance(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_distance(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_distance(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_distance(to_geopoint(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); }); describe('st_intersects', () => { @@ -7092,6 +7569,106 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval st_intersects(null, null)', []); testErrorsAndWarnings('row nullVar = null | eval st_intersects(nullVar, nullVar)', []); + + testErrorsAndWarnings( + 'row var = st_intersects(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_intersects(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_intersects(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_intersects(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_intersects(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_geopoint(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_intersects(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_geopoint(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_intersects(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_geoshape(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_intersects(to_geoshape(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); }); describe('st_within', () => { @@ -7328,59 +7905,170 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval st_within(null, null)', []); testErrorsAndWarnings('row nullVar = null | eval st_within(nullVar, nullVar)', []); - }); - describe('st_x', () => { - testErrorsAndWarnings('row var = st_x(cartesianPointField)', [ - 'Unknown column [cartesianPointField]', - ]); - testErrorsAndWarnings('row st_x(cartesianPointField)', [ - 'Unknown column [cartesianPointField]', - ]); - testErrorsAndWarnings('row var = st_x(to_cartesianpoint(cartesianPointField))', [ - 'Unknown column [cartesianPointField]', - ]); - testErrorsAndWarnings('row var = st_x(geoPointField)', ['Unknown column [geoPointField]']); - testErrorsAndWarnings('row st_x(geoPointField)', ['Unknown column [geoPointField]']); - testErrorsAndWarnings('row var = st_x(to_geopoint(geoPointField))', [ - 'Unknown column [geoPointField]', - ]); + testErrorsAndWarnings( + 'row var = st_within(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); - testErrorsAndWarnings('row var = st_x(true)', [ - 'Argument of [st_x] must be [cartesian_point], found value [true] type [boolean]', - ]); + testErrorsAndWarnings( + 'row st_within(to_cartesianpoint("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); - testErrorsAndWarnings('from a_index | eval var = st_x(cartesianPointField)', []); - testErrorsAndWarnings('from a_index | eval st_x(cartesianPointField)', []); + testErrorsAndWarnings( + 'row var = st_within(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); testErrorsAndWarnings( - 'from a_index | eval var = st_x(to_cartesianpoint(cartesianPointField))', + 'row var = st_within(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', [] ); - testErrorsAndWarnings('from a_index | eval st_x(booleanField)', [ - 'Argument of [st_x] must be [cartesian_point], found value [booleanField] type [boolean]', - ]); + testErrorsAndWarnings( + 'row st_within(to_cartesianpoint("POINT (30 10)"), to_cartesianshape("POINT (30 10)"))', + [] + ); - testErrorsAndWarnings('from a_index | eval var = st_x(*)', [ - 'Using wildcards (*) in st_x is not allowed', - ]); + testErrorsAndWarnings( + 'row var = st_within(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); - testErrorsAndWarnings('from a_index | eval var = st_x(geoPointField)', []); - testErrorsAndWarnings('from a_index | eval st_x(geoPointField)', []); - testErrorsAndWarnings('from a_index | eval var = st_x(to_geopoint(geoPointField))', []); + testErrorsAndWarnings( + 'row var = st_within(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); - testErrorsAndWarnings('from a_index | eval st_x(cartesianPointField, extraArg)', [ - 'Error: [st_x] function expects exactly one argument, got 2.', - ]); + testErrorsAndWarnings( + 'row st_within(to_cartesianshape("POINT (30 10)"), to_cartesianpoint("POINT (30 10)"))', + [] + ); - testErrorsAndWarnings('from a_index | sort st_x(cartesianPointField)', []); - testErrorsAndWarnings('from a_index | eval st_x(null)', []); - testErrorsAndWarnings('row nullVar = null | eval st_x(nullVar)', []); - }); + testErrorsAndWarnings( + 'row var = st_within(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); - describe('st_y', () => { - testErrorsAndWarnings('row var = st_y(cartesianPointField)', [ + testErrorsAndWarnings( + 'row var = st_within(to_cartesianshape(to_cartesianpoint("POINT (30 10)")), to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_within(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_within(to_geopoint("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_within(to_geopoint(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_within(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_within(to_geopoint("POINT (30 10)"), to_geoshape("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_within(to_geopoint(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_within(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row st_within(to_geoshape("POINT (30 10)"), to_geopoint("POINT (30 10)"))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_within(to_geoshape(to_geopoint("POINT (30 10)")), to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = st_within(to_geoshape(to_geopoint("POINT (30 10)")), to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); + }); + + describe('st_x', () => { + testErrorsAndWarnings('row var = st_x(cartesianPointField)', [ + 'Unknown column [cartesianPointField]', + ]); + testErrorsAndWarnings('row st_x(cartesianPointField)', [ + 'Unknown column [cartesianPointField]', + ]); + testErrorsAndWarnings('row var = st_x(to_cartesianpoint(cartesianPointField))', [ + 'Unknown column [cartesianPointField]', + ]); + testErrorsAndWarnings('row var = st_x(geoPointField)', ['Unknown column [geoPointField]']); + testErrorsAndWarnings('row st_x(geoPointField)', ['Unknown column [geoPointField]']); + testErrorsAndWarnings('row var = st_x(to_geopoint(geoPointField))', [ + 'Unknown column [geoPointField]', + ]); + + testErrorsAndWarnings('row var = st_x(true)', [ + 'Argument of [st_x] must be [cartesian_point], found value [true] type [boolean]', + ]); + + testErrorsAndWarnings('from a_index | eval var = st_x(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval st_x(cartesianPointField)', []); + + testErrorsAndWarnings( + 'from a_index | eval var = st_x(to_cartesianpoint(cartesianPointField))', + [] + ); + + testErrorsAndWarnings('from a_index | eval st_x(booleanField)', [ + 'Argument of [st_x] must be [cartesian_point], found value [booleanField] type [boolean]', + ]); + + testErrorsAndWarnings('from a_index | eval var = st_x(*)', [ + 'Using wildcards (*) in st_x is not allowed', + ]); + + testErrorsAndWarnings('from a_index | eval var = st_x(geoPointField)', []); + testErrorsAndWarnings('from a_index | eval st_x(geoPointField)', []); + testErrorsAndWarnings('from a_index | eval var = st_x(to_geopoint(geoPointField))', []); + + testErrorsAndWarnings('from a_index | eval st_x(cartesianPointField, extraArg)', [ + 'Error: [st_x] function expects exactly one argument, got 2.', + ]); + + testErrorsAndWarnings('from a_index | sort st_x(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval st_x(null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_x(nullVar)', []); + testErrorsAndWarnings('row var = st_x(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row st_x(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = st_x(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings('row var = st_x(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row st_x(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = st_x(to_geopoint(to_geopoint("POINT (30 10)")))', []); + }); + + describe('st_y', () => { + testErrorsAndWarnings('row var = st_y(cartesianPointField)', [ 'Unknown column [cartesianPointField]', ]); testErrorsAndWarnings('row st_y(cartesianPointField)', [ @@ -7426,6 +8114,17 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort st_y(cartesianPointField)', []); testErrorsAndWarnings('from a_index | eval st_y(null)', []); testErrorsAndWarnings('row nullVar = null | eval st_y(nullVar)', []); + testErrorsAndWarnings('row var = st_y(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row st_y(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = st_y(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings('row var = st_y(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row st_y(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = st_y(to_geopoint(to_geopoint("POINT (30 10)")))', []); }); describe('starts_with', () => { @@ -7465,6 +8164,10 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort starts_with(keywordField, keywordField)', []); testErrorsAndWarnings('from a_index | eval starts_with(null, null)', []); testErrorsAndWarnings('row nullVar = null | eval starts_with(nullVar, nullVar)', []); + testErrorsAndWarnings('from a_index | eval var = starts_with(keywordField, textField)', []); + testErrorsAndWarnings('from a_index | eval starts_with(keywordField, textField)', []); + testErrorsAndWarnings('from a_index | eval var = starts_with(textField, keywordField)', []); + testErrorsAndWarnings('from a_index | eval starts_with(textField, keywordField)', []); }); describe('substring', () => { @@ -7799,6 +8502,16 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort to_cartesianpoint(cartesianPointField)', []); testErrorsAndWarnings('from a_index | eval to_cartesianpoint(null)', []); testErrorsAndWarnings('row nullVar = null | eval to_cartesianpoint(nullVar)', []); + testErrorsAndWarnings( + 'row var = to_cartesianpoint(to_cartesianpoint("POINT (30 10)"))', + [] + ); + testErrorsAndWarnings('row to_cartesianpoint(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = to_cartesianpoint(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); }); describe('to_cartesianshape', () => { @@ -7876,6 +8589,21 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort to_cartesianshape(cartesianPointField)', []); testErrorsAndWarnings('from a_index | eval to_cartesianshape(null)', []); testErrorsAndWarnings('row nullVar = null | eval to_cartesianshape(nullVar)', []); + testErrorsAndWarnings( + 'row var = to_cartesianshape(to_cartesianpoint("POINT (30 10)"))', + [] + ); + testErrorsAndWarnings('row to_cartesianshape(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = to_cartesianshape(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = to_cartesianshape(to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); }); describe('to_datetime', () => { @@ -8154,6 +8882,12 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort to_geopoint(geoPointField)', []); testErrorsAndWarnings('from a_index | eval to_geopoint(null)', []); testErrorsAndWarnings('row nullVar = null | eval to_geopoint(nullVar)', []); + testErrorsAndWarnings('row var = to_geopoint(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row to_geopoint(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings( + 'row var = to_geopoint(to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); }); describe('to_geoshape', () => { @@ -8211,6 +8945,16 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort to_geoshape(geoPointField)', []); testErrorsAndWarnings('from a_index | eval to_geoshape(null)', []); testErrorsAndWarnings('row nullVar = null | eval to_geoshape(nullVar)', []); + testErrorsAndWarnings('row var = to_geoshape(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row to_geoshape(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings( + 'row var = to_geoshape(to_geopoint(to_geopoint("POINT (30 10)")))', + [] + ); + testErrorsAndWarnings( + 'row var = to_geoshape(to_geoshape(to_geopoint("POINT (30 10)")))', + [] + ); }); describe('to_integer', () => { @@ -11367,8 +12111,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats var = max(dateField)', []); testErrorsAndWarnings('from a_index | stats max(dateField)', []); - testErrorsAndWarnings('from a_index | stats var = max(datePeriodField)', []); - testErrorsAndWarnings('from a_index | stats max(datePeriodField)', []); testErrorsAndWarnings('from a_index | stats var = max(booleanField)', []); testErrorsAndWarnings('from a_index | stats max(booleanField)', []); testErrorsAndWarnings('from a_index | stats var = max(ipField)', []); @@ -11410,14 +12152,6 @@ describe('validation logic', () => { 'WHERE does not support function max', ]); - testErrorsAndWarnings('from a_index | where max(datePeriodField)', [ - 'WHERE does not support function max', - ]); - - testErrorsAndWarnings('from a_index | where max(datePeriodField) > 0', [ - 'WHERE does not support function max', - ]); - testErrorsAndWarnings('from a_index | where max(booleanField)', [ 'WHERE does not support function max', ]); @@ -11497,23 +12231,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval max(dateField) > 0', [ 'EVAL does not support function max', ]); - - testErrorsAndWarnings('from a_index | eval var = max(datePeriodField)', [ - 'EVAL does not support function max', - ]); - - testErrorsAndWarnings('from a_index | eval var = max(datePeriodField) > 0', [ - 'EVAL does not support function max', - ]); - - testErrorsAndWarnings('from a_index | eval max(datePeriodField)', [ - 'EVAL does not support function max', - ]); - - testErrorsAndWarnings('from a_index | eval max(datePeriodField) > 0', [ - 'EVAL does not support function max', - ]); - testErrorsAndWarnings('from a_index | eval var = max(booleanField)', [ 'EVAL does not support function max', ]); @@ -11716,8 +12433,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats var = min(dateField)', []); testErrorsAndWarnings('from a_index | stats min(dateField)', []); - testErrorsAndWarnings('from a_index | stats var = min(datePeriodField)', []); - testErrorsAndWarnings('from a_index | stats min(datePeriodField)', []); testErrorsAndWarnings('from a_index | stats var = min(booleanField)', []); testErrorsAndWarnings('from a_index | stats min(booleanField)', []); testErrorsAndWarnings('from a_index | stats var = min(ipField)', []); @@ -11758,15 +12473,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | where min(dateField) > 0', [ 'WHERE does not support function min', ]); - - testErrorsAndWarnings('from a_index | where min(datePeriodField)', [ - 'WHERE does not support function min', - ]); - - testErrorsAndWarnings('from a_index | where min(datePeriodField) > 0', [ - 'WHERE does not support function min', - ]); - testErrorsAndWarnings('from a_index | where min(booleanField)', [ 'WHERE does not support function min', ]); @@ -11847,22 +12553,6 @@ describe('validation logic', () => { 'EVAL does not support function min', ]); - testErrorsAndWarnings('from a_index | eval var = min(datePeriodField)', [ - 'EVAL does not support function min', - ]); - - testErrorsAndWarnings('from a_index | eval var = min(datePeriodField) > 0', [ - 'EVAL does not support function min', - ]); - - testErrorsAndWarnings('from a_index | eval min(datePeriodField)', [ - 'EVAL does not support function min', - ]); - - testErrorsAndWarnings('from a_index | eval min(datePeriodField) > 0', [ - 'EVAL does not support function min', - ]); - testErrorsAndWarnings('from a_index | eval var = min(booleanField)', [ 'EVAL does not support function min', ]); @@ -11961,6 +12651,71 @@ describe('validation logic', () => { 'row nullVar = null | stats count_distinct(nullVar, nullVar, nullVar, nullVar, nullVar, nullVar, nullVar, nullVar)', [] ); + + testErrorsAndWarnings( + 'from a_index | stats var = count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + [] + ); + + testErrorsAndWarnings( + 'from a_index | stats count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + [] + ); + + testErrorsAndWarnings( + 'from a_index | stats var = round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField))', + [] + ); + + testErrorsAndWarnings( + 'from a_index | stats round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField))', + [] + ); + + testErrorsAndWarnings( + 'from a_index | stats var = round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)) + count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + [] + ); + + testErrorsAndWarnings( + 'from a_index | stats round(count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)) + count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + [] + ); + + testErrorsAndWarnings( + 'from a_index | sort count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + ['SORT does not support function count_distinct'] + ); + + testErrorsAndWarnings( + 'from a_index | where count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + ['WHERE does not support function count_distinct'] + ); + + testErrorsAndWarnings( + 'from a_index | where count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField) > 0', + ['WHERE does not support function count_distinct'] + ); + + testErrorsAndWarnings( + 'from a_index | eval var = count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + ['EVAL does not support function count_distinct'] + ); + + testErrorsAndWarnings( + 'from a_index | eval var = count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField) > 0', + ['EVAL does not support function count_distinct'] + ); + + testErrorsAndWarnings( + 'from a_index | eval count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField)', + ['EVAL does not support function count_distinct'] + ); + + testErrorsAndWarnings( + 'from a_index | eval count_distinct(textField, integerField, counterIntegerField, doubleField, unsignedLongField, longField, counterLongField, counterDoubleField) > 0', + ['EVAL does not support function count_distinct'] + ); }); describe('st_centroid_agg', () => { @@ -13037,157 +13792,676 @@ describe('validation logic', () => { ]); testErrorsAndWarnings( - 'from a_index | eval var = weighted_avg(longField, integerField) > 0', - ['EVAL does not support function weighted_avg'] + 'from a_index | eval var = weighted_avg(longField, integerField) > 0', + ['EVAL does not support function weighted_avg'] + ); + + testErrorsAndWarnings('from a_index | eval weighted_avg(longField, integerField)', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings('from a_index | eval weighted_avg(longField, integerField) > 0', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings('from a_index | eval var = weighted_avg(integerField, doubleField)', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings( + 'from a_index | eval var = weighted_avg(integerField, doubleField) > 0', + ['EVAL does not support function weighted_avg'] + ); + + testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, doubleField)', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, doubleField) > 0', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings('from a_index | eval var = weighted_avg(integerField, longField)', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings( + 'from a_index | eval var = weighted_avg(integerField, longField) > 0', + ['EVAL does not support function weighted_avg'] + ); + + testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, longField)', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, longField) > 0', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings( + 'from a_index | eval var = weighted_avg(integerField, integerField)', + ['EVAL does not support function weighted_avg'] + ); + + testErrorsAndWarnings( + 'from a_index | eval var = weighted_avg(integerField, integerField) > 0', + ['EVAL does not support function weighted_avg'] + ); + + testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, integerField)', [ + 'EVAL does not support function weighted_avg', + ]); + + testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, integerField) > 0', [ + 'EVAL does not support function weighted_avg', + ]); + }); + + describe('bucket', () => { + testErrorsAndWarnings('from a_index | stats by bucket(dateField, 1 year)', []); + testErrorsAndWarnings('from a_index | stats by bin(dateField, 1 year)', []); + + testErrorsAndWarnings('from a_index | stats by bucket(integerField, integerField)', [ + 'Argument of [bucket] must be a constant, received [integerField]', + ]); + + testErrorsAndWarnings('from a_index | stats by bin(integerField, integerField)', [ + 'Argument of [bin] must be a constant, received [integerField]', + ]); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(dateField, integerField, textField, textField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(dateField, integerField, textField, textField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [textField]', + 'Argument of [bin] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(dateField, integerField, dateField, dateField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [dateField]', + 'Argument of [bucket] must be a constant, received [dateField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(dateField, integerField, dateField, dateField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [dateField]', + 'Argument of [bin] must be a constant, received [dateField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(dateField, integerField, textField, dateField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [dateField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(dateField, integerField, textField, dateField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [textField]', + 'Argument of [bin] must be a constant, received [dateField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(dateField, integerField, dateField, textField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [dateField]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(dateField, integerField, dateField, textField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [dateField]', + 'Argument of [bin] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(integerField, integerField, integerField, integerField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, integerField, integerField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings('from a_index | sort bucket(dateField, 1 year)', [ + 'SORT does not support function bucket', + ]); + + testErrorsAndWarnings('from a_index | stats bucket(null, null, null, null)', []); + + testErrorsAndWarnings( + 'row nullVar = null | stats bucket(nullVar, nullVar, nullVar, nullVar)', + [ + 'Argument of [bucket] must be a constant, received [nullVar]', + 'Argument of [bucket] must be a constant, received [nullVar]', + 'Argument of [bucket] must be a constant, received [nullVar]', + ] + ); + + testErrorsAndWarnings('from a_index | stats bucket("2022", 1 year)', []); + testErrorsAndWarnings('from a_index | stats bucket(concat("20", "22"), 1 year)', [ + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + ]); + + testErrorsAndWarnings( + 'from a_index | stats bucket("2022", integerField, textField, textField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats bucket(concat("20", "22"), integerField, textField, textField)', + [ + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings('from a_index | stats bucket("2022", integerField, "2022", "2022")', [ + 'Argument of [bucket] must be a constant, received [integerField]', + ]); + + testErrorsAndWarnings( + 'from a_index | stats bucket(concat("20", "22"), integerField, concat("20", "22"), concat("20", "22"))', + [ + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats bucket("2022", integerField, textField, "2022")', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats bucket(concat("20", "22"), integerField, textField, concat("20", "22"))', + [ + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats bucket("2022", integerField, "2022", textField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats bucket(concat("20", "22"), integerField, concat("20", "22"), textField)', + [ + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bucket] must be a constant, received [textField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(dateField, integerField, now(), now())', + ['Argument of [bucket] must be a constant, received [integerField]'] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(dateField, integerField, now(), now())', + ['Argument of [bin] must be a constant, received [integerField]'] + ); + + testErrorsAndWarnings('from a_index | stats by bucket(doubleField, doubleField)', [ + 'Argument of [bucket] must be a constant, received [doubleField]', + ]); + + testErrorsAndWarnings('from a_index | stats by bin(doubleField, doubleField)', [ + 'Argument of [bin] must be a constant, received [doubleField]', + ]); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, doubleField, doubleField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, doubleField, doubleField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, doubleField, integerField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, doubleField, integerField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, doubleField, longField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [longField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, doubleField, longField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [longField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, integerField, doubleField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, integerField, doubleField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, integerField, integerField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, integerField, integerField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, integerField, longField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, integerField, longField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, longField, doubleField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, longField, doubleField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, longField, integerField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, longField, integerField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [integerField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(doubleField, integerField, longField, longField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [longField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(doubleField, integerField, longField, longField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [longField]', + ] + ); + + testErrorsAndWarnings('from a_index | stats by bucket(integerField, doubleField)', [ + 'Argument of [bucket] must be a constant, received [doubleField]', + ]); + + testErrorsAndWarnings('from a_index | stats by bin(integerField, doubleField)', [ + 'Argument of [bin] must be a constant, received [doubleField]', + ]); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(integerField, integerField, doubleField, doubleField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, doubleField, doubleField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [doubleField]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | stats by bucket(integerField, integerField, doubleField, integerField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [integerField]', + ] ); - testErrorsAndWarnings('from a_index | eval weighted_avg(longField, integerField)', [ - 'EVAL does not support function weighted_avg', - ]); - - testErrorsAndWarnings('from a_index | eval weighted_avg(longField, integerField) > 0', [ - 'EVAL does not support function weighted_avg', - ]); - - testErrorsAndWarnings('from a_index | eval var = weighted_avg(integerField, doubleField)', [ - 'EVAL does not support function weighted_avg', - ]); + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, doubleField, integerField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [integerField]', + ] + ); testErrorsAndWarnings( - 'from a_index | eval var = weighted_avg(integerField, doubleField) > 0', - ['EVAL does not support function weighted_avg'] + 'from a_index | stats by bucket(integerField, integerField, doubleField, longField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [longField]', + ] ); - testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, doubleField)', [ - 'EVAL does not support function weighted_avg', - ]); + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, doubleField, longField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [longField]', + ] + ); - testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, doubleField) > 0', [ - 'EVAL does not support function weighted_avg', - ]); + testErrorsAndWarnings( + 'from a_index | stats by bucket(integerField, integerField, integerField, doubleField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + ] + ); - testErrorsAndWarnings('from a_index | eval var = weighted_avg(integerField, longField)', [ - 'EVAL does not support function weighted_avg', - ]); + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, integerField, doubleField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', + ] + ); testErrorsAndWarnings( - 'from a_index | eval var = weighted_avg(integerField, longField) > 0', - ['EVAL does not support function weighted_avg'] + 'from a_index | stats by bucket(integerField, integerField, integerField, longField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + ] ); - testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, longField)', [ - 'EVAL does not support function weighted_avg', - ]); + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, integerField, longField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + ] + ); - testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, longField) > 0', [ - 'EVAL does not support function weighted_avg', - ]); + testErrorsAndWarnings( + 'from a_index | stats by bucket(integerField, integerField, longField, doubleField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + ] + ); testErrorsAndWarnings( - 'from a_index | eval var = weighted_avg(integerField, integerField)', - ['EVAL does not support function weighted_avg'] + 'from a_index | stats by bin(integerField, integerField, longField, doubleField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [doubleField]', + ] ); testErrorsAndWarnings( - 'from a_index | eval var = weighted_avg(integerField, integerField) > 0', - ['EVAL does not support function weighted_avg'] + 'from a_index | stats by bucket(integerField, integerField, longField, integerField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [integerField]', + ] ); - testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, integerField)', [ - 'EVAL does not support function weighted_avg', - ]); + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, longField, integerField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [integerField]', + ] + ); - testErrorsAndWarnings('from a_index | eval weighted_avg(integerField, integerField) > 0', [ - 'EVAL does not support function weighted_avg', - ]); - }); + testErrorsAndWarnings( + 'from a_index | stats by bucket(integerField, integerField, longField, longField)', + [ + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [longField]', + ] + ); - describe('bucket', () => { - testErrorsAndWarnings('from a_index | stats by bucket(dateField, 1 year)', []); - testErrorsAndWarnings('from a_index | stats by bin(dateField, 1 year)', []); + testErrorsAndWarnings( + 'from a_index | stats by bin(integerField, integerField, longField, longField)', + [ + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [longField]', + ] + ); - testErrorsAndWarnings('from a_index | stats by bucket(integerField, integerField)', [ - 'Argument of [bucket] must be a constant, received [integerField]', + testErrorsAndWarnings('from a_index | stats by bucket(longField, doubleField)', [ + 'Argument of [bucket] must be a constant, received [doubleField]', ]); - testErrorsAndWarnings('from a_index | stats by bin(integerField, integerField)', [ - 'Argument of [bin] must be a constant, received [integerField]', + testErrorsAndWarnings('from a_index | stats by bin(longField, doubleField)', [ + 'Argument of [bin] must be a constant, received [doubleField]', ]); testErrorsAndWarnings( - 'from a_index | stats by bucket(dateField, integerField, textField, textField)', + 'from a_index | stats by bucket(longField, integerField, doubleField, doubleField)', [ 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [textField]', - 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [doubleField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bin(dateField, integerField, textField, textField)', + 'from a_index | stats by bin(longField, integerField, doubleField, doubleField)', [ 'Argument of [bin] must be a constant, received [integerField]', - 'Argument of [bin] must be a constant, received [textField]', - 'Argument of [bin] must be a constant, received [textField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [doubleField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bucket(dateField, integerField, dateField, dateField)', + 'from a_index | stats by bucket(longField, integerField, doubleField, integerField)', [ 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [dateField]', - 'Argument of [bucket] must be a constant, received [dateField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [integerField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bin(dateField, integerField, dateField, dateField)', + 'from a_index | stats by bin(longField, integerField, doubleField, integerField)', [ 'Argument of [bin] must be a constant, received [integerField]', - 'Argument of [bin] must be a constant, received [dateField]', - 'Argument of [bin] must be a constant, received [dateField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [integerField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bucket(dateField, integerField, textField, dateField)', + 'from a_index | stats by bucket(longField, integerField, doubleField, longField)', [ 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [textField]', - 'Argument of [bucket] must be a constant, received [dateField]', + 'Argument of [bucket] must be a constant, received [doubleField]', + 'Argument of [bucket] must be a constant, received [longField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bin(dateField, integerField, textField, dateField)', + 'from a_index | stats by bin(longField, integerField, doubleField, longField)', [ 'Argument of [bin] must be a constant, received [integerField]', - 'Argument of [bin] must be a constant, received [textField]', - 'Argument of [bin] must be a constant, received [dateField]', + 'Argument of [bin] must be a constant, received [doubleField]', + 'Argument of [bin] must be a constant, received [longField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bucket(dateField, integerField, dateField, textField)', + 'from a_index | stats by bucket(longField, integerField, integerField, doubleField)', [ 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [dateField]', - 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [doubleField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bin(dateField, integerField, dateField, textField)', + 'from a_index | stats by bin(longField, integerField, integerField, doubleField)', [ 'Argument of [bin] must be a constant, received [integerField]', - 'Argument of [bin] must be a constant, received [dateField]', - 'Argument of [bin] must be a constant, received [textField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [doubleField]', ] ); testErrorsAndWarnings( - 'from a_index | stats by bucket(integerField, integerField, integerField, integerField)', + 'from a_index | stats by bucket(longField, integerField, integerField, integerField)', [ 'Argument of [bucket] must be a constant, received [integerField]', 'Argument of [bucket] must be a constant, received [integerField]', @@ -13196,7 +14470,7 @@ describe('validation logic', () => { ); testErrorsAndWarnings( - 'from a_index | stats by bin(integerField, integerField, integerField, integerField)', + 'from a_index | stats by bin(longField, integerField, integerField, integerField)', [ 'Argument of [bin] must be a constant, received [integerField]', 'Argument of [bin] must be a constant, received [integerField]', @@ -13204,92 +14478,75 @@ describe('validation logic', () => { ] ); - testErrorsAndWarnings('from a_index | sort bucket(dateField, 1 year)', [ - 'SORT does not support function bucket', - ]); - - testErrorsAndWarnings('from a_index | stats bucket(null, null, null, null)', []); - testErrorsAndWarnings( - 'row nullVar = null | stats bucket(nullVar, nullVar, nullVar, nullVar)', + 'from a_index | stats by bucket(longField, integerField, integerField, longField)', [ - 'Argument of [bucket] must be a constant, received [nullVar]', - 'Argument of [bucket] must be a constant, received [nullVar]', - 'Argument of [bucket] must be a constant, received [nullVar]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [integerField]', + 'Argument of [bucket] must be a constant, received [longField]', ] ); - testErrorsAndWarnings('from a_index | stats bucket("2022", 1 year)', []); - testErrorsAndWarnings('from a_index | stats bucket(concat("20", "22"), 1 year)', [ - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', - ]); - testErrorsAndWarnings( - 'from a_index | stats bucket("2022", integerField, textField, textField)', + 'from a_index | stats by bin(longField, integerField, integerField, longField)', [ - 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [textField]', - 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', ] ); testErrorsAndWarnings( - 'from a_index | stats bucket(concat("20", "22"), integerField, textField, textField)', + 'from a_index | stats by bucket(longField, integerField, longField, doubleField)', [ - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [textField]', - 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [doubleField]', ] ); - testErrorsAndWarnings('from a_index | stats bucket("2022", integerField, "2022", "2022")', [ - 'Argument of [bucket] must be a constant, received [integerField]', - ]); - testErrorsAndWarnings( - 'from a_index | stats bucket(concat("20", "22"), integerField, concat("20", "22"), concat("20", "22"))', + 'from a_index | stats by bin(longField, integerField, longField, doubleField)', [ - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', - 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [doubleField]', ] ); testErrorsAndWarnings( - 'from a_index | stats bucket("2022", integerField, textField, "2022")', + 'from a_index | stats by bucket(longField, integerField, longField, integerField)', [ 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [integerField]', ] ); testErrorsAndWarnings( - 'from a_index | stats bucket(concat("20", "22"), integerField, textField, concat("20", "22"))', + 'from a_index | stats by bin(longField, integerField, longField, integerField)', [ - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', - 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [textField]', - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [integerField]', ] ); testErrorsAndWarnings( - 'from a_index | stats bucket("2022", integerField, "2022", textField)', + 'from a_index | stats by bucket(longField, integerField, longField, longField)', [ 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bucket] must be a constant, received [longField]', + 'Argument of [bucket] must be a constant, received [longField]', ] ); testErrorsAndWarnings( - 'from a_index | stats bucket(concat("20", "22"), integerField, concat("20", "22"), textField)', + 'from a_index | stats by bin(longField, integerField, longField, longField)', [ - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', - 'Argument of [bucket] must be a constant, received [integerField]', - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', - 'Argument of [bucket] must be a constant, received [textField]', + 'Argument of [bin] must be a constant, received [integerField]', + 'Argument of [bin] must be a constant, received [longField]', + 'Argument of [bin] must be a constant, received [longField]', ] ); }); @@ -14448,6 +15705,91 @@ describe('validation logic', () => { testErrorsAndWarnings('row nullVar = null | eval to_string(nullVar)', []); testErrorsAndWarnings('from a_index | eval to_string("2022")', []); testErrorsAndWarnings('from a_index | eval to_string(concat("20", "22"))', []); + testErrorsAndWarnings('row var = to_string(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row to_string(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = to_str(to_cartesianpoint("POINT (30 10)"))', []); + + testErrorsAndWarnings( + 'row var = to_string(to_cartesianpoint(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings( + 'row var = to_string(to_cartesianshape(to_cartesianpoint("POINT (30 10)")))', + [] + ); + + testErrorsAndWarnings('row var = to_string(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row to_string(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = to_str(to_geopoint("POINT (30 10)"))', []); + testErrorsAndWarnings('row var = to_string(to_geopoint(to_geopoint("POINT (30 10)")))', []); + testErrorsAndWarnings('row var = to_string(to_geoshape(to_geopoint("POINT (30 10)")))', []); + }); + + describe('mv_pseries_weighted_sum', () => { + testErrorsAndWarnings('row var = mv_pseries_weighted_sum(5.5, 5.5)', []); + testErrorsAndWarnings('row mv_pseries_weighted_sum(5.5, 5.5)', []); + testErrorsAndWarnings( + 'row var = mv_pseries_weighted_sum(to_double(true), to_double(true))', + [] + ); + + testErrorsAndWarnings('row var = mv_pseries_weighted_sum(true, true)', [ + 'Argument of [mv_pseries_weighted_sum] must be [double], found value [true] type [boolean]', + 'Argument of [mv_pseries_weighted_sum] must be [double], found value [true] type [boolean]', + ]); + + testErrorsAndWarnings( + 'from a_index | where mv_pseries_weighted_sum(doubleField, doubleField) > 0', + [] + ); + + testErrorsAndWarnings( + 'from a_index | where mv_pseries_weighted_sum(booleanField, booleanField) > 0', + [ + 'Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]', + 'Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | eval var = mv_pseries_weighted_sum(doubleField, doubleField)', + [] + ); + + testErrorsAndWarnings( + 'from a_index | eval mv_pseries_weighted_sum(doubleField, doubleField)', + [] + ); + + testErrorsAndWarnings( + 'from a_index | eval var = mv_pseries_weighted_sum(to_double(booleanField), to_double(booleanField))', + [] + ); + + testErrorsAndWarnings( + 'from a_index | eval mv_pseries_weighted_sum(booleanField, booleanField)', + [ + 'Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]', + 'Argument of [mv_pseries_weighted_sum] must be [double], found value [booleanField] type [boolean]', + ] + ); + + testErrorsAndWarnings( + 'from a_index | eval mv_pseries_weighted_sum(doubleField, doubleField, extraArg)', + ['Error: [mv_pseries_weighted_sum] function expects exactly 2 arguments, got 3.'] + ); + + testErrorsAndWarnings( + 'from a_index | sort mv_pseries_weighted_sum(doubleField, doubleField)', + [] + ); + + testErrorsAndWarnings('from a_index | eval mv_pseries_weighted_sum(null, null)', []); + testErrorsAndWarnings( + 'row nullVar = null | eval mv_pseries_weighted_sum(nullVar, nullVar)', + [] + ); }); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts index 6cc515dc4ac74..4de1adf4a3153 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts @@ -111,7 +111,7 @@ function validateFunctionLiteralArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: argDef.type, + argType: argDef.type as string, value: typeof actualArg.value === 'number' ? actualArg.value : String(actualArg.value), givenType: actualArg.literalType, }, @@ -139,7 +139,7 @@ function validateFunctionLiteralArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: argDef.type, + argType: argDef.type as string, value: actualArg.name, givenType: 'duration', }, @@ -169,7 +169,7 @@ function validateInlineCastArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: parameterDefinition.type, + argType: parameterDefinition.type as string, value: arg.text, givenType: arg.castType, }, @@ -206,7 +206,7 @@ function validateNestedFunctionArg( messages.push( getMessageFromId({ messageId: 'noNestedArgumentSupport', - values: { name: actualArg.text, argType: argFn.signatures[0].returnType }, + values: { name: actualArg.text, argType: argFn.signatures[0].returnType as string }, locations: actualArg.location, }) ); @@ -219,9 +219,9 @@ function validateNestedFunctionArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: parameterDefinition.type, + argType: parameterDefinition.type as string, value: actualArg.text, - givenType: argFn.signatures[0].returnType, + givenType: argFn.signatures[0].returnType as string, }, locations: actualArg.location, }) @@ -301,7 +301,7 @@ function validateFunctionColumnArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: parameterDefinition.type, + argType: parameterDefinition.type as string, value: actualArg.name, givenType: columnHit!.type, }, @@ -464,7 +464,7 @@ function validateFunction( allMatchingArgDefinitionsAreConstantOnly || forceConstantOnly, // use the nesting flag for now just for stats and metrics // TODO: revisit this part later on to make it more generic - parentCommand === 'stats' || parentCommand === 'metrics' + ['stats', 'inlinestats', 'metrics'].includes(parentCommand) ? isNested || !isAssignment(astFunction) : false ); @@ -534,14 +534,14 @@ function validateFunction( }); }); - const shouldCollapseMessages = isArrayType(argDef.type) && hasMultipleElements; + const shouldCollapseMessages = isArrayType(argDef.type as string) && hasMultipleElements; failingSignature.push( ...(shouldCollapseMessages ? collapseWrongArgumentTypeMessages( messagesFromAllArgElements, outerArg, astFunction.name, - argDef.type, + argDef.type as string, parentCommand, references ) @@ -1010,7 +1010,7 @@ function validateCommand(command: ESQLCommand, references: ReferenceMaps): ESQLM ); } if (isColumnItem(arg)) { - if (command.name === 'stats') { + if (command.name === 'stats' || command.name === 'inlinestats') { messages.push(errors.unknownAggFunction(arg)); } else { messages.push(...validateColumnForCommand(arg, command.name, references)); @@ -1076,15 +1076,17 @@ function validateUnsupportedTypeFields(fields: Map<string, ESQLRealField>) { 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 }, - }) - ); + // Removed temporarily to supress all these warnings + // Issue to re-enable in a better way: https://github.com/elastic/kibana/issues/189666 + // messages.push( + // getMessageFromId({ + // messageId: 'unsupportedFieldType', + // values: { + // field: field.name, + // }, + // locations: { min: 1, max: 1 }, + // }) + // ); } } return messages; diff --git a/packages/kbn-ftr-common-functional-services/index.ts b/packages/kbn-ftr-common-functional-services/index.ts index 2fabcc12227c5..4bd3eca34c45c 100644 --- a/packages/kbn-ftr-common-functional-services/index.ts +++ b/packages/kbn-ftr-common-functional-services/index.ts @@ -23,4 +23,6 @@ export type Es = ProvidedType<typeof EsProvider>; import { SupertestWithoutAuthProvider } from './services/supertest_without_auth'; export type SupertestWithoutAuthProviderType = ProvidedType<typeof SupertestWithoutAuthProvider>; +export type { InternalRequestHeader, RoleCredentials } from './services/saml_auth'; + export type { FtrProviderContext } from './services/ftr_provider_context'; diff --git a/packages/kbn-ftr-common-functional-services/services/all.ts b/packages/kbn-ftr-common-functional-services/services/all.ts index 128d50731081d..49308faeb3dd0 100644 --- a/packages/kbn-ftr-common-functional-services/services/all.ts +++ b/packages/kbn-ftr-common-functional-services/services/all.ts @@ -11,6 +11,7 @@ import { EsProvider } from './es'; import { KibanaServerProvider } from './kibana_server'; import { RetryService } from './retry'; import { SupertestWithoutAuthProvider } from './supertest_without_auth'; +import { SamlAuthProvider } from './saml_auth'; export const services = { es: EsProvider, @@ -18,4 +19,5 @@ export const services = { esArchiver: EsArchiverProvider, retry: RetryService, supertestWithoutAuth: SupertestWithoutAuthProvider, + samlAuth: SamlAuthProvider, }; diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/default_request_headers.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/default_request_headers.ts new file mode 100644 index 0000000000000..95983647647f3 --- /dev/null +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/default_request_headers.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. + */ + +export const COMMON_REQUEST_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', +}; + +// possible change in 9.0 to match serverless +const STATEFUL_INTERNAL_REQUEST_HEADERS = { + ...COMMON_REQUEST_HEADERS, +}; + +const SERVERLESS_INTERNAL_REQUEST_HEADERS = { + ...COMMON_REQUEST_HEADERS, + 'x-elastic-internal-origin': 'kibana', +}; + +export type InternalRequestHeader = + | typeof STATEFUL_INTERNAL_REQUEST_HEADERS + | typeof SERVERLESS_INTERNAL_REQUEST_HEADERS; + +export const getServerlessInternalRequestHeaders = (): InternalRequestHeader => { + return SERVERLESS_INTERNAL_REQUEST_HEADERS; +}; + +export const getStatefulInternalRequestHeaders = (): InternalRequestHeader => { + return STATEFUL_INTERNAL_REQUEST_HEADERS; +}; diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/get_auth_provider.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/get_auth_provider.ts new file mode 100644 index 0000000000000..3f1ec7af917ff --- /dev/null +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/get_auth_provider.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 fs from 'fs'; +import { type Config } from '@kbn/test'; +import { ToolingLog } from '@kbn/tooling-log'; +import { MOCK_IDP_REALM_NAME } from '@kbn/mock-idp-utils'; +import { KibanaServer } from '../..'; + +import { ServerlessAuthProvider } from './serverless/auth_provider'; +import { StatefulAuthProvider } from './stateful/auth_provider'; +import { createRole, createRoleMapping } from './stateful/create_role_mapping'; + +const STATEFUL_ADMIN_ROLE_MAPPING_PATH = './stateful/admin_mapping'; + +export interface AuthProvider { + getSupportedRoleDescriptors(): any; + getDefaultRole(): string; + getRolesDefinitionPath(): string; + getCommonRequestHeader(): { [key: string]: string }; + getInternalRequestHeader(): { [key: string]: string }; +} + +export interface AuthProviderProps { + config: Config; + kibanaServer: KibanaServer; + log: ToolingLog; +} + +export const getAuthProvider = async (props: AuthProviderProps) => { + const { config, log, kibanaServer } = props; + const isServerless = !!props.config.get('serverless'); + if (isServerless) { + return new ServerlessAuthProvider(config); + } + + const provider = new StatefulAuthProvider(); + // TODO: Move it to @kbn-es package, so that roles and its mapping are created before FTR services loading starts. + // 'viewer' and 'editor' roles are available by default, but we have to create 'admin' role + const adminRoleMapping = JSON.parse( + fs.readFileSync(require.resolve(STATEFUL_ADMIN_ROLE_MAPPING_PATH), 'utf8') + ); + await createRole({ roleName: 'admin', roleMapping: adminRoleMapping, kibanaServer, log }); + const roles = Object.keys(provider.getSupportedRoleDescriptors()); + // Creating roles mapping for mock-idp + await createRoleMapping({ name: MOCK_IDP_REALM_NAME, roles, config, log }); + return provider; +}; diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/index.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/index.ts new file mode 100644 index 0000000000000..15735d4bffcbb --- /dev/null +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/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 { SamlAuthProvider } from './saml_auth_provider'; +export type { RoleCredentials } from './saml_auth_provider'; +export type { InternalRequestHeader } from './default_request_headers'; diff --git a/x-pack/test_serverless/shared/services/svl_user_manager.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts similarity index 61% rename from x-pack/test_serverless/shared/services/svl_user_manager.ts rename to packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts index 2bb8cbc5fab40..4e79cad656197 100644 --- a/x-pack/test_serverless/shared/services/svl_user_manager.ts +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/saml_auth_provider.ts @@ -1,18 +1,18 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may 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 { ServerlessProjectType, SERVERLESS_ROLES_ROOT_PATH } from '@kbn/es'; import { SamlSessionManager } from '@kbn/test'; -import { readRolesDescriptorsFromResource } from '@kbn/es'; -import { resolve } from 'path'; -import { Role } from '@kbn/test/src/auth/types'; -import { isServerlessProjectType } from '@kbn/es/src/utils'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../functional/ftr_provider_context'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { resolve } from 'path'; +import { FtrProviderContext } from '../ftr_provider_context'; +import { getAuthProvider } from './get_auth_provider'; +import { InternalRequestHeader } from './default_request_headers'; export interface RoleCredentials { apiKey: { id: string; name: string }; @@ -20,63 +20,41 @@ export interface RoleCredentials { cookieHeader: { Cookie: string }; } -export function SvlUserManagerProvider({ getService }: FtrProviderContext) { +export async function SamlAuthProvider({ getService }: FtrProviderContext) { const config = getService('config'); const log = getService('log'); - const svlCommonApi = getService('svlCommonApi'); + const kibanaServer = getService('kibanaServer'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const isCloud = !!process.env.TEST_CLOUD; - const kbnServerArgs = config.get('kbnTestServer.serverArgs') as string[]; - const projectType = kbnServerArgs - .filter((arg) => arg.startsWith('--serverless')) - .reduce((acc, arg) => { - const match = arg.match(/--serverless[=\s](\w+)/); - return acc + (match ? match[1] : ''); - }, '') as ServerlessProjectType; - - if (!isServerlessProjectType(projectType)) { - throw new Error(`Unsupported serverless projectType: ${projectType}`); - } - - const supportedRoleDescriptors = readRolesDescriptorsFromResource( - resolve(SERVERLESS_ROLES_ROOT_PATH, projectType, 'roles.yml') - ); + const authRoleProvider = await getAuthProvider({ config, kibanaServer, log }); + const supportedRoleDescriptors = authRoleProvider.getSupportedRoleDescriptors(); const supportedRoles = Object.keys(supportedRoleDescriptors); - const defaultRolesToMap = new Map<string, Role>([ - ['es', 'developer'], - ['security', 'editor'], - ['oblt', 'editor'], - ]); - - const getDefaultRole = () => { - if (defaultRolesToMap.has(projectType)) { - return defaultRolesToMap.get(projectType)!; - } else { - throw new Error(`Default role is not defined for ${projectType} project`); - } - }; - const customRolesFileName: string | undefined = process.env.ROLES_FILENAME_OVERRIDE; + const cloudUsersFilePath = resolve(REPO_ROOT, '.ftr', customRolesFileName ?? 'role_users.json'); + // Sharing the instance within FTR config run means cookies are persistent for each role between tests. - const sessionManager = new SamlSessionManager( - { - hostOptions: { - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: isCloud ? undefined : config.get('servers.kibana.port'), - username: config.get('servers.kibana.username'), - password: config.get('servers.kibana.password'), - }, - log, - isCloud, - supportedRoles, + const sessionManager = new SamlSessionManager({ + hostOptions: { + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: isCloud ? undefined : config.get('servers.kibana.port'), + username: config.get('servers.kibana.username'), + password: config.get('servers.kibana.password'), }, - customRolesFileName - ); + log, + isCloud, + supportedRoles: { + roles: supportedRoles, + sourcePath: authRoleProvider.getRolesDefinitionPath(), + }, + cloudUsersFilePath, + }); - const DEFAULT_ROLE = getDefaultRole(); + const DEFAULT_ROLE = authRoleProvider.getDefaultRole(); + const COMMON_REQUEST_HEADERS = authRoleProvider.getCommonRequestHeader(); + const INTERNAL_REQUEST_HEADERS = authRoleProvider.getInternalRequestHeader(); return { async getInteractiveUserSessionCookieWithRoleScope(role: string) { @@ -119,7 +97,7 @@ export function SvlUserManagerProvider({ getService }: FtrProviderContext) { const { body, status } = await supertestWithoutAuth .post('/internal/security/api_key') - .set(svlCommonApi.getInternalRequestHeader()) + .set(INTERNAL_REQUEST_HEADERS) .set(adminCookieHeader) .send({ name: 'myTestApiKey', @@ -147,12 +125,19 @@ export function SvlUserManagerProvider({ getService }: FtrProviderContext) { const { status } = await supertestWithoutAuth .post('/internal/security/api_key/invalidate') - .set(svlCommonApi.getInternalRequestHeader()) + .set(INTERNAL_REQUEST_HEADERS) .set(roleCredentials.cookieHeader) .send(requestBody); expect(status).to.be(200); }, + getCommonRequestHeader() { + return COMMON_REQUEST_HEADERS; + }, + + getInternalRequestHeader(): InternalRequestHeader { + return INTERNAL_REQUEST_HEADERS; + }, DEFAULT_ROLE, }; } diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/serverless/auth_provider.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/serverless/auth_provider.ts new file mode 100644 index 0000000000000..eecbe3a5862f2 --- /dev/null +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/serverless/auth_provider.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 { ServerlessProjectType, SERVERLESS_ROLES_ROOT_PATH } from '@kbn/es'; +import { type Config } from '@kbn/test'; +import { isServerlessProjectType, readRolesDescriptorsFromResource } from '@kbn/es/src/utils'; +import { resolve } from 'path'; +import { Role } from '@kbn/test/src/auth/types'; +import { + getServerlessInternalRequestHeaders, + COMMON_REQUEST_HEADERS, +} from '../default_request_headers'; +import { AuthProvider } from '../get_auth_provider'; + +const projectDefaultRoles = new Map<string, Role>([ + ['es', 'developer'], + ['security', 'editor'], + ['oblt', 'editor'], +]); + +const getDefaultServerlessRole = (projectType: string) => { + if (projectDefaultRoles.has(projectType)) { + return projectDefaultRoles.get(projectType)!; + } else { + throw new Error(`Default role is not defined for ${projectType} project`); + } +}; + +export class ServerlessAuthProvider implements AuthProvider { + private readonly projectType: string; + private readonly rolesDefinitionPath: string; + + constructor(config: Config) { + const kbnServerArgs = config.get('kbnTestServer.serverArgs') as string[]; + this.projectType = kbnServerArgs.reduce((acc, arg) => { + const match = arg.match(/--serverless[=\s](\w+)/); + return acc + (match ? match[1] : ''); + }, '') as ServerlessProjectType; + + if (!isServerlessProjectType(this.projectType)) { + throw new Error(`Unsupported serverless projectType: ${this.projectType}`); + } + + this.rolesDefinitionPath = resolve(SERVERLESS_ROLES_ROOT_PATH, this.projectType, 'roles.yml'); + } + + getSupportedRoleDescriptors(): any { + return readRolesDescriptorsFromResource(this.rolesDefinitionPath); + } + getDefaultRole(): string { + return getDefaultServerlessRole(this.projectType); + } + getRolesDefinitionPath(): string { + return this.rolesDefinitionPath; + } + getCommonRequestHeader() { + return COMMON_REQUEST_HEADERS; + } + getInternalRequestHeader() { + return getServerlessInternalRequestHeaders(); + } +} diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/admin_mapping.json b/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/admin_mapping.json new file mode 100644 index 0000000000000..f8544f16bd239 --- /dev/null +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/admin_mapping.json @@ -0,0 +1,46 @@ +{ + "kibana":[ + { + "base":[ + "all" + ], + "feature":{ + + }, + "spaces":[ + "*" + ] + } + ], + "elasticsearch":{ + "cluster":[ + "all" + ], + "indices":[ + { + "names":[ + "*" + ], + "privileges":[ + "all" + ], + "allow_restricted_indices":false + }, + { + "names":[ + "*" + ], + "privileges":[ + "monitor", + "read", + "read_cross_cluster", + "view_index_metadata" + ], + "allow_restricted_indices":true + } + ], + "run_as":[ + + ] + } +} \ No newline at end of file diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/auth_provider.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/auth_provider.ts new file mode 100644 index 0000000000000..cf27fd9d5d506 --- /dev/null +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/auth_provider.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { readRolesDescriptorsFromResource, STATEFUL_ROLES_ROOT_PATH } from '@kbn/es'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { resolve } from 'path'; +import { AuthProvider } from '../get_auth_provider'; +import { + getStatefulInternalRequestHeaders, + COMMON_REQUEST_HEADERS, +} from '../default_request_headers'; + +export class StatefulAuthProvider implements AuthProvider { + private readonly rolesDefinitionPath = resolve(REPO_ROOT, STATEFUL_ROLES_ROOT_PATH, 'roles.yml'); + getSupportedRoleDescriptors(): any { + return readRolesDescriptorsFromResource(this.rolesDefinitionPath); + } + getDefaultRole() { + return 'editor'; + } + getRolesDefinitionPath() { + return this.rolesDefinitionPath; + } + + getCommonRequestHeader() { + return COMMON_REQUEST_HEADERS; + } + + getInternalRequestHeader() { + return getStatefulInternalRequestHeaders(); + } +} diff --git a/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/create_role_mapping.ts b/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/create_role_mapping.ts new file mode 100644 index 0000000000000..1e9b897362dc6 --- /dev/null +++ b/packages/kbn-ftr-common-functional-services/services/saml_auth/stateful/create_role_mapping.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 { Config, createEsClientForFtrConfig } from '@kbn/test'; +import { ToolingLog } from '@kbn/tooling-log'; +import { KibanaServer } from '../../..'; + +export interface CreateRoleProps { + roleName: string; + roleMapping: string[]; + kibanaServer: KibanaServer; + log: ToolingLog; +} + +export interface CreateRoleMappingProps { + name: string; + roles: string[]; + config: Config; + log: ToolingLog; +} + +export async function createRole(props: CreateRoleProps) { + const { roleName, roleMapping, kibanaServer, log } = props; + log.debug(`Adding a role: ${roleName}`); + const { status, statusText } = await kibanaServer.request({ + path: `/api/security/role/${roleName}`, + method: 'PUT', + body: roleMapping, + retries: 0, + }); + if (status !== 204) { + throw new Error(`Expected status code of 204, received ${status} ${statusText}`); + } +} + +export async function createRoleMapping(props: CreateRoleMappingProps) { + const { name, roles, config, log } = props; + log.debug(`Creating a role mapping: {realm.name: ${name}, roles: ${roles}}`); + const esClient = createEsClientForFtrConfig(config); + await esClient.security.putRoleMapping({ + name, + roles, + enabled: true, + // @ts-ignore + rules: { field: { 'realm.name': name } }, + }); +} diff --git a/packages/kbn-ftr-common-functional-services/tsconfig.json b/packages/kbn-ftr-common-functional-services/tsconfig.json index 3641c807e4d6d..ff10ec6c9d5fb 100644 --- a/packages/kbn-ftr-common-functional-services/tsconfig.json +++ b/packages/kbn-ftr-common-functional-services/tsconfig.json @@ -14,7 +14,11 @@ "@kbn/core-saved-objects-server", "@kbn/tooling-log", "@kbn/es-archiver", - "@kbn/test" + "@kbn/test", + "@kbn/expect", + "@kbn/repo-info", + "@kbn/es", + "@kbn/mock-idp-utils" ], "exclude": [ "target/**/*", diff --git a/packages/kbn-management/cards_navigation/src/consts.tsx b/packages/kbn-management/cards_navigation/src/consts.tsx index ba0d370c9577f..0215c66d1b09d 100644 --- a/packages/kbn-management/cards_navigation/src/consts.tsx +++ b/packages/kbn-management/cards_navigation/src/consts.tsx @@ -97,6 +97,13 @@ export const appDefinitions: Record<AppId, AppDefinition> = { icon: 'wrench', }, + [AppIds.SPACES]: { + category: appCategories.CONTENT, + description: i18n.translate('management.landing.withCardNavigation.spacesDescription', { + defaultMessage: 'Organize your saved objects into meaningful categories.', + }), + icon: 'spaces', + }, [AppIds.SAVED_OBJECTS]: { category: appCategories.CONTENT, description: i18n.translate('management.landing.withCardNavigation.objectsDescription', { diff --git a/packages/kbn-management/cards_navigation/src/types.ts b/packages/kbn-management/cards_navigation/src/types.ts index 438275b246529..24e4e73624e73 100644 --- a/packages/kbn-management/cards_navigation/src/types.ts +++ b/packages/kbn-management/cards_navigation/src/types.ts @@ -29,6 +29,7 @@ export enum AppIds { ROLES = 'roles', API_KEYS = 'api_keys', DATA_QUALITY = 'data_quality', + SPACES = 'spaces', } // Create new type that is a union of all the appId values diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts index b914451abdb55..2d09f5a2b2ad7 100644 --- a/packages/kbn-management/settings/setting_ids/index.ts +++ b/packages/kbn-management/settings/setting_ids/index.ts @@ -143,6 +143,14 @@ export const OBSERVABILITY_LOGS_EXPLORER_ALLOWED_DATA_VIEWS_ID = 'observability:logsExplorer:allowedDataViews'; export const OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE = 'observability:entityCentricExperience'; export const OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID = 'observability:logSources'; +export const OBSERVABILITY_AI_ASSISTANT_LOGS_INDEX_PATTERN_ID = + 'observability:aiAssistantLogsIndexPattern'; +export const OBSERVABILITY_AI_ASSISTANT_RESPONSE_LANGUAGE = + 'observability:aiAssistantResponseLanguage'; +export const OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING = + 'observability:aiAssistantSimulatedFunctionCalling'; +export const OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN = + 'observability:aiAssistantSearchConnectorIndexPattern'; // Reporting settings export const XPACK_REPORTING_CUSTOM_PDF_LOGO_ID = 'xpackReporting:customPdfLogo'; diff --git a/packages/kbn-monaco/src/esql/lib/esql_theme.ts b/packages/kbn-monaco/src/esql/lib/esql_theme.ts index 1e2028d672e1d..12e39b4e6e7ef 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_theme.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_theme.ts @@ -60,6 +60,7 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ 'metadata', 'mv_expand', 'stats', + 'inlinestats', 'dissect', 'grok', 'project', diff --git a/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts b/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts index 141653c2b2cf6..d49fb150d7c12 100644 --- a/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts +++ b/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts @@ -11,17 +11,21 @@ import { getHoverItem } from './hover'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; import { ENRICH_MODES, + ESQLRealField, getFunctionDefinition, getFunctionSignatures, } from '@kbn/esql-validation-autocomplete'; +import { FieldType } from '@kbn/esql-validation-autocomplete/src/definitions/types'; -const fields: Array<{ name: string; type: string; suggestedAs?: string }> = [ - ...['string', 'number', 'date', 'boolean', 'ip'].map((type) => ({ +const types: FieldType[] = ['keyword', 'double', 'date', 'boolean', 'ip']; + +const fields: Array<ESQLRealField & { suggestedAs?: string }> = [ + ...types.map((type) => ({ name: `${type}Field`, type, })), - { name: 'any#Char$Field', type: 'number', suggestedAs: '`any#Char$Field`' }, - { name: 'kubernetes.something.something', type: 'number' }, + { name: 'any#Char$Field', type: 'double', suggestedAs: '`any#Char$Field`' }, + { name: 'kubernetes.something.something', type: 'double' }, ]; const indexes = ( @@ -56,7 +60,7 @@ const policies = [ ]; function createCustomCallbackMocks( - customFields: Array<{ name: string; type: string }> | undefined, + customFields: ESQLRealField[] | undefined, customSources: Array<{ name: string; hidden: boolean }> | undefined, customPolicies: | Array<{ diff --git a/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts b/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts index 44deada7cd155..251c00d4a75b4 100644 --- a/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts +++ b/packages/kbn-securitysolution-utils/src/esql/compute_if_esql_query_aggregating.ts @@ -6,11 +6,18 @@ * Side Public License, v 1. */ +import { ESQLAst, getAstAndSyntaxErrors } from '@kbn/esql-ast'; + +export const isAggregatingQuery = (ast: ESQLAst): boolean => { + return ast.some((astItem) => astItem.type === 'command' && astItem.name === 'stats'); +}; + /** * compute if esqlQuery is aggregating/grouping, i.e. using STATS...BY command * @param esqlQuery * @returns boolean */ export const computeIsESQLQueryAggregating = (esqlQuery: string): boolean => { - return /\|\s+stats\s/i.test(esqlQuery); + const { ast } = getAstAndSyntaxErrors(esqlQuery); + return isAggregatingQuery(ast); }; diff --git a/packages/kbn-securitysolution-utils/tsconfig.json b/packages/kbn-securitysolution-utils/tsconfig.json index c734b5c153fb0..5b9520c487e31 100644 --- a/packages/kbn-securitysolution-utils/tsconfig.json +++ b/packages/kbn-securitysolution-utils/tsconfig.json @@ -12,7 +12,8 @@ ], "kbn_references": [ "@kbn/i18n", - "@kbn/esql-utils" + "@kbn/esql-utils", + "@kbn/esql-ast" ], "exclude": [ "target/**/*", diff --git a/packages/kbn-test/src/auth/helper.ts b/packages/kbn-test/src/auth/helper.ts index fc32eab773c8e..3c8374911af8c 100644 --- a/packages/kbn-test/src/auth/helper.ts +++ b/packages/kbn-test/src/auth/helper.ts @@ -10,12 +10,13 @@ import * as fs from 'fs'; import { Role, User } from './types'; export const readCloudUsersFromFile = (filePath: string): Array<[Role, User]> => { + const defaultMessage = `Cannot read roles and email/password from ${filePath}`; if (!fs.existsSync(filePath)) { - throw new Error(`Please define user roles with email/password in ${filePath}`); + throw new Error(`${defaultMessage}: file does not exist`); } const data = fs.readFileSync(filePath, 'utf8'); if (data.length === 0) { - throw new Error(`'${filePath}' is empty: no roles are defined`); + throw new Error(`${defaultMessage}: file is empty`); } return Object.entries(JSON.parse(data)) as Array<[Role, User]>; diff --git a/packages/kbn-test/src/auth/session_manager.ts b/packages/kbn-test/src/auth/session_manager.ts index 40beccdfc3c6c..fb12e181e8a1e 100644 --- a/packages/kbn-test/src/auth/session_manager.ts +++ b/packages/kbn-test/src/auth/session_manager.ts @@ -6,10 +6,7 @@ * Side Public License, v 1. */ -import { SERVERLESS_ROLES_ROOT_PATH } from '@kbn/es'; -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'; @@ -32,31 +29,32 @@ export interface HostOptions { export interface SamlSessionManagerOptions { hostOptions: HostOptions; isCloud: boolean; - supportedRoles?: string[]; + supportedRoles?: SupportedRoles; + cloudUsersFilePath: string; log: ToolingLog; } +export interface SupportedRoles { + sourcePath: string; + roles: string[]; +} + /** * Manages cookies associated with user roles */ export class SamlSessionManager { - private readonly DEFAULT_ROLES_FILE_NAME: string = 'role_users.json'; private readonly isCloud: boolean; private readonly kbnHost: string; private readonly kbnClient: KbnClient; private readonly log: ToolingLog; private readonly roleToUserMap: Map<Role, User>; private readonly sessionCache: Map<Role, Session>; - private readonly supportedRoles: string[]; - private readonly userRoleFilePath: string; + private readonly supportedRoles?: SupportedRoles; + private readonly cloudUsersFilePath: string; - constructor(options: SamlSessionManagerOptions, rolesFilename?: string) { + constructor(options: SamlSessionManagerOptions) { this.isCloud = options.isCloud; this.log = options.log; - // if the rolesFilename is provided, respect it. Otherwise use DEFAULT_ROLES_FILE_NAME. - const rolesFile = rolesFilename ? rolesFilename : this.DEFAULT_ROLES_FILE_NAME; - this.log.info(`Using the file ${rolesFile} for the role users`); - this.userRoleFilePath = resolve(REPO_ROOT, '.ftr', rolesFile); const hostOptionsWithoutAuth = { protocol: options.hostOptions.protocol, hostname: options.hostOptions.hostname, @@ -70,9 +68,10 @@ export class SamlSessionManager { auth: `${options.hostOptions.username}:${options.hostOptions.password}`, }), }); + this.cloudUsersFilePath = options.cloudUsersFilePath; this.sessionCache = new Map<Role, Session>(); this.roleToUserMap = new Map<Role, User>(); - this.supportedRoles = options.supportedRoles ?? []; + this.supportedRoles = options.supportedRoles; } /** @@ -81,7 +80,8 @@ export class SamlSessionManager { */ private getCloudUsers = () => { if (this.roleToUserMap.size === 0) { - const data = readCloudUsersFromFile(this.userRoleFilePath); + this.log.info(`Reading cloud user credentials from ${this.cloudUsersFilePath}`); + const data = readCloudUsersFromFile(this.cloudUsersFilePath); for (const [roleName, user] of data) { this.roleToUserMap.set(roleName, user); } @@ -104,11 +104,11 @@ export class SamlSessionManager { } // Validate role before creating SAML session - if (this.supportedRoles.length && !this.supportedRoles.includes(role)) { + if (this.supportedRoles && !this.supportedRoles.roles.includes(role)) { throw new Error( - `Role '${role}' is not defined in the supported list: ${this.supportedRoles.join( + `Role '${role}' is not in the supported list: ${this.supportedRoles.roles.join( ', ' - )}. Update roles resource file in ${SERVERLESS_ROLES_ROOT_PATH} to enable it for testing` + )}. Add role descriptor in ${this.supportedRoles.sourcePath} to enable it for testing` ); } diff --git a/packages/kbn-test/src/auth/sesson_manager.test.ts b/packages/kbn-test/src/auth/sesson_manager.test.ts index 1f04620584507..929517e7c5a10 100644 --- a/packages/kbn-test/src/auth/sesson_manager.test.ts +++ b/packages/kbn-test/src/auth/sesson_manager.test.ts @@ -9,17 +9,23 @@ import { ToolingLog } from '@kbn/tooling-log'; import { Cookie } from 'tough-cookie'; import { Session } from './saml_auth'; -import { SamlSessionManager } from './session_manager'; +import { SamlSessionManager, SupportedRoles } from './session_manager'; import * as samlAuth from './saml_auth'; import * as helper from './helper'; import { Role, User, UserProfile } from './types'; import { SERVERLESS_ROLES_ROOT_PATH } from '@kbn/es'; +import { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; const log = new ToolingLog(); -const supportedRoles = ['admin', 'editor', 'viewer']; +const supportedRoles: SupportedRoles = { + roles: ['admin', 'editor', 'viewer'], + sourcePath: 'test/roles.yml', +}; const roleViewer = 'viewer'; const roleEditor = 'editor'; +const cloudUsersFilePath = resolve(REPO_ROOT, SERVERLESS_ROLES_ROOT_PATH, 'role_users.json'); const createLocalSAMLSessionMock = jest.spyOn(samlAuth, 'createLocalSAMLSession'); const createCloudSAMLSessionMock = jest.spyOn(samlAuth, 'createCloudSAMLSession'); @@ -58,7 +64,7 @@ describe('SamlSessionManager', () => { hostOptions, isCloud, log, - supportedRoles, + cloudUsersFilePath, }; const testEmail = 'testuser@elastic.com'; const testFullname = 'Test User'; @@ -67,7 +73,7 @@ describe('SamlSessionManager', () => { )!; test('should create an instance of SamlSessionManager', () => { - const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + const samlSessionManager = new SamlSessionManager(samlSessionManagerOptions); expect(samlSessionManager).toBeInstanceOf(SamlSessionManager); }); @@ -118,10 +124,13 @@ describe('SamlSessionManager', () => { test(`throws error when role is not in 'supportedRoles'`, async () => { const nonExistingRole = 'tester'; - const expectedErrorMessage = `Role '${nonExistingRole}' is not defined in the supported list: ${supportedRoles.join( + const expectedErrorMessage = `Role '${nonExistingRole}' is not in the supported list: ${supportedRoles.roles.join( ', ' - )}. Update roles resource file in ${SERVERLESS_ROLES_ROOT_PATH} to enable it for testing`; - const samlSessionManager = new SamlSessionManager(samlSessionManagerOptions); + )}. Add role descriptor in ${supportedRoles.sourcePath} to enable it for testing`; + const samlSessionManager = new SamlSessionManager({ + ...samlSessionManagerOptions, + supportedRoles, + }); await expect( samlSessionManager.getInteractiveUserSessionCookieWithRoleScope(nonExistingRole) ).rejects.toThrow(expectedErrorMessage); @@ -145,11 +154,7 @@ describe('SamlSessionManager', () => { elastic_cloud_user: false, }; getSecurityProfileMock.mockResolvedValueOnce(testData); - const samlSessionManager = new SamlSessionManager({ - hostOptions, - log, - isCloud, - }); + const samlSessionManager = new SamlSessionManager(samlSessionManagerOptions); await samlSessionManager.getInteractiveUserSessionCookieWithRoleScope(nonExistingRole); await samlSessionManager.getApiCredentialsForRole(nonExistingRole); await samlSessionManager.getUserData(nonExistingRole); @@ -171,7 +176,7 @@ describe('SamlSessionManager', () => { hostOptions, isCloud, log, - supportedRoles, + cloudUsersFilePath, }; const cloudCookieInstance = Cookie.parse( 'sid=cloud_cookie_value; Path=/; Expires=Wed, 01 Oct 2023 07:00:00 GMT' @@ -195,11 +200,7 @@ describe('SamlSessionManager', () => { test('should throw error if TEST_CLOUD_HOST_NAME is not set', async () => { isValidHostnameMock.mockReturnValueOnce(false); - const samlSessionManager = new SamlSessionManager({ - hostOptions, - log, - isCloud, - }); + const samlSessionManager = new SamlSessionManager(samlSessionManagerOptions); await expect( samlSessionManager.getInteractiveUserSessionCookieWithRoleScope(roleViewer) ).rejects.toThrow( @@ -220,11 +221,7 @@ describe('SamlSessionManager', () => { }); test('should create an instance of SamlSessionManager', () => { - const samlSessionManager = new SamlSessionManager({ - hostOptions, - log, - isCloud, - }); + const samlSessionManager = new SamlSessionManager(samlSessionManagerOptions); expect(samlSessionManager).toBeInstanceOf(SamlSessionManager); }); @@ -276,10 +273,13 @@ describe('SamlSessionManager', () => { test(`throws error for non-existing role when 'supportedRoles' is defined`, async () => { const nonExistingRole = 'tester'; - const expectedErrorMessage = `Role '${nonExistingRole}' is not defined in the supported list: ${supportedRoles.join( + const expectedErrorMessage = `Role '${nonExistingRole}' is not in the supported list: ${supportedRoles.roles.join( ', ' - )}. Update roles resource file in ${SERVERLESS_ROLES_ROOT_PATH} to enable it for testing`; - const samlSessionManager = new SamlSessionManager(samlSessionManagerOptions); + )}. Add role descriptor in ${supportedRoles.sourcePath} to enable it for testing`; + const samlSessionManager = new SamlSessionManager({ + ...samlSessionManagerOptions, + supportedRoles, + }); await expect( samlSessionManager.getInteractiveUserSessionCookieWithRoleScope(nonExistingRole) ).rejects.toThrow(expectedErrorMessage); @@ -294,11 +294,7 @@ describe('SamlSessionManager', () => { test(`throws error for non-existing role when 'supportedRoles' is not defined`, async () => { const nonExistingRole = 'tester'; - const samlSessionManager = new SamlSessionManager({ - hostOptions, - log, - isCloud, - }); + const samlSessionManager = new SamlSessionManager(samlSessionManagerOptions); await expect( samlSessionManager.getInteractiveUserSessionCookieWithRoleScope(nonExistingRole) ).rejects.toThrow(`User with '${nonExistingRole}' role is not defined`); diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts b/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts index 7176d21b8f0b3..309cac56ca30c 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/run_check_ftr_configs_cli.ts @@ -125,7 +125,8 @@ export async function runCheckFtrConfigsCli() { const invalid = possibleConfigs.filter((path) => !allFtrConfigs.includes(path)); if (invalid.length) { - const invalidList = invalid.map((path) => Path.relative(REPO_ROOT, path)).join('\n - '); + const invalidList = + ' - ' + invalid.map((path) => Path.relative(REPO_ROOT, path)).join('\n - '); log.error( `The following files look like FTR configs which are not listed in one of manifest files:\n${invalidList}\n Make sure to add your new FTR config to the correct manifest file.\n diff --git a/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts b/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts index 4cfebe1597607..511d14d2da3f0 100644 --- a/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts +++ b/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts @@ -7,15 +7,15 @@ */ import { getColumnsWithMetadata } from './ecs_metadata_helper'; -import type { DatatableColumnType } from '@kbn/expressions-plugin/common'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; +import { ESQLRealField } from '@kbn/esql-validation-autocomplete'; describe('getColumnsWithMetadata', () => { it('should return original columns if fieldsMetadata is not provided', async () => { - const columns = [ - { name: 'ecs.version', type: 'keyword' as DatatableColumnType }, - { name: 'field1', type: 'text' as DatatableColumnType }, - { name: 'field2', type: 'double' as DatatableColumnType }, + const columns: ESQLRealField[] = [ + { name: 'ecs.version', type: 'keyword' }, + { name: 'field1', type: 'text' }, + { name: 'field2', type: 'double' }, ]; const result = await getColumnsWithMetadata(columns); @@ -23,10 +23,10 @@ describe('getColumnsWithMetadata', () => { }); it('should return columns with metadata if both name and type match with ECS fields', async () => { - const columns = [ - { name: 'ecs.field', type: 'text' as DatatableColumnType }, - { name: 'ecs.fakeBooleanField', type: 'boolean' as DatatableColumnType }, - { name: 'field2', type: 'double' as DatatableColumnType }, + const columns: ESQLRealField[] = [ + { name: 'ecs.field', type: 'text' }, + { name: 'ecs.fakeBooleanField', type: 'boolean' }, + { name: 'field2', type: 'double' }, ]; const fieldsMetadata = { getClient: jest.fn().mockResolvedValue({ @@ -57,10 +57,10 @@ describe('getColumnsWithMetadata', () => { }); it('should handle keyword suffix correctly', async () => { - const columns = [ - { name: 'ecs.version', type: 'keyword' as DatatableColumnType }, - { name: 'ecs.version.keyword', type: 'keyword' as DatatableColumnType }, - { name: 'field2', type: 'double' as DatatableColumnType }, + const columns: ESQLRealField[] = [ + { name: 'ecs.version', type: 'keyword' }, + { name: 'ecs.version.keyword', type: 'keyword' }, + { name: 'field2', type: 'double' }, ]; const fieldsMetadata = { getClient: jest.fn().mockResolvedValue({ diff --git a/packages/kbn-text-based-editor/src/esql_documentation_sections.tsx b/packages/kbn-text-based-editor/src/esql_documentation_sections.tsx index 6dbcbad824ddd..05cb7b452c18b 100644 --- a/packages/kbn-text-based-editor/src/esql_documentation_sections.tsx +++ b/packages/kbn-text-based-editor/src/esql_documentation_sections.tsx @@ -2176,6 +2176,40 @@ export const functions = { ROW a=[2, 1] | EVAL min_a = MV_MIN(a) \`\`\` + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + ignoreTag: true, + } + )} + /> + ), + }, + // Do not edit manually... automatically generated by scripts/generate_esql_docs.ts + { + label: i18n.translate( + 'textBasedEditor.query.textBasedLanguagesEditor.documentationESQL.mv_pseries_weighted_sum', + { + defaultMessage: 'MV_PSERIES_WEIGHTED_SUM', + } + ), + description: ( + <Markdown + markdownContent={i18n.translate( + 'textBasedEditor.query.textBasedLanguagesEditor.documentationESQL.mv_pseries_weighted_sum.markdown', + { + defaultMessage: `<!-- + This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it. + --> + + ### MV_PSERIES_WEIGHTED_SUM + Converts a multivalued expression into a single-valued column by multiplying every element on the input list by its corresponding term in P-Series and computing the sum. + + \`\`\` + ROW a = [70.0, 45.0, 21.0, 21.0, 21.0] + | EVAL sum = MV_PSERIES_WEIGHTED_SUM(a, 1.5) + | KEEP sum + \`\`\` `, description: 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', 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 8b92bb213157e..326dc471a86c1 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 @@ -29,6 +29,8 @@ import memoize from 'lodash/memoize'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { css } from '@emotion/react'; +import { ESQLRealField } from '@kbn/esql-validation-autocomplete'; +import { FieldType } from '@kbn/esql-validation-autocomplete/src/definitions/types'; import { EditorFooter } from './editor_footer'; import { fetchFieldsFromESQL } from './fetch_fields_from_esql'; import { @@ -322,13 +324,12 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ undefined, abortController ).result; - const columns = + const columns: ESQLRealField[] = table?.columns.map((c) => { - // Casting unsupported as unknown to avoid plethora of warnings - // Remove when addressed https://github.com/elastic/kibana/issues/189666 - if (!c.meta.esType || c.meta.esType === 'unsupported') - return { name: c.name, type: 'unknown' }; - return { name: c.name, type: c.meta.esType }; + return { + name: c.name, + type: c.meta.esType as FieldType, + }; }) || []; return await getRateLimitedColumnsWithMetadata(columns, fieldsMetadata); } catch (e) { @@ -763,8 +764,18 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ forceMoveMarkers: true, }, ]); + setPopoverPosition({}); + datePickerOpenStatusRef.current = false; + + // move the cursor past the date we just inserted + editor1.current?.setPosition({ + lineNumber: currentCursorPosition?.lineNumber ?? 0, + column: (currentCursorPosition?.column ?? 0) + addition.length - 1, + }); + // restore focus to the editor + editor1.current?.focus(); } }} inline diff --git a/packages/kbn-unified-data-table/README.md b/packages/kbn-unified-data-table/README.md index 576a676289d7a..7a2db17781ad1 100644 --- a/packages/kbn-unified-data-table/README.md +++ b/packages/kbn-unified-data-table/README.md @@ -41,13 +41,12 @@ Props description: | **configRowHeight** | (optional)number | Optional value for providing configuration setting for UnifiedDataTable rows height. | | **showMultiFields** | (optional)boolean | Optional value for providing configuration setting for enabling to display the complex fields in the table. Default is true. | | **maxDocFieldsDisplayed** | (optional)number | Optional value for providing configuration setting for maximum number of document fields to display in the table. Default is 50. | -| **externalControlColumns** | (optional)EuiDataGridControlColumn[] | Optional value for providing EuiDataGridControlColumn list of the additional leading control columns. UnifiedDataTable includes two control columns: Open Details and Select. | +| **rowAdditionalLeadingControls** | (optional)RowControlColumn[] | Optional value for providing an list of the additional leading control columns. UnifiedDataTable includes two control columns: Open Details and Select. | | **totalHits** | (optional)number | Number total hits from ES. | | **onFetchMoreRecords** | (optional)() => void | To fetch more. | | **externalAdditionalControls** | (optional)React.ReactNode | Optional value for providing the additional controls available in the UnifiedDataTable toolbar to manage it's records or state. UnifiedDataTable includes Columns, Sorting and Bulk Actions. | | **rowsPerPageOptions** | (optional)number[] | Optional list of number type values to set custom UnifiedDataTable paging options to display the records per page. | | **renderCustomGridBody** | (optional)(args: EuiDataGridCustomBodyProps) => React.ReactNode; | An optional function called to completely customize and control the rendering of EuiDataGrid's body and cell placement. | -| **trailingControlColumns** | (optional)EuiDataGridControlColumn[] | An optional list of the EuiDataGridControlColumn type for setting trailing control columns standard for EuiDataGrid. | | **visibleCellActions** | (optional)number | An optional value for a custom number of the visible cell actions in the table. By default is up to 3. | | **externalCustomRenderers** | (optional)Record<string,(props: EuiDataGridCellValueElementProps) => React.ReactNode>; | An optional settings for a specified fields rendering like links. Applied only for the listed fields rendering. | | **consumer** | (optional)string | Name of the UnifiedDataTable consumer component or application. | @@ -141,9 +140,7 @@ Usage example: [browserFields, handleOnPanelClosed, runtimeMappings, timelineId] ); } - externalControlColumns={leadingControlColumns} externalAdditionalControls={additionalControls} - trailingControlColumns={trailingControlColumns} renderCustomGridBody={renderCustomGridBody} rowsPerPageOptions={[10, 30, 40, 100]} showFullScreenButton={false} diff --git a/packages/kbn-unified-data-table/__mocks__/external_control_columns.tsx b/packages/kbn-unified-data-table/__mocks__/external_control_columns.tsx index d67afccc01559..dce5e13a3c89d 100644 --- a/packages/kbn-unified-data-table/__mocks__/external_control_columns.tsx +++ b/packages/kbn-unified-data-table/__mocks__/external_control_columns.tsx @@ -17,6 +17,7 @@ import { EuiSpacer, EuiDataGridControlColumn, } from '@elastic/eui'; +import type { RowControlColumn } from '../src/types'; const SelectionHeaderCell = () => { return ( @@ -116,3 +117,22 @@ export const testLeadingControlColumn: EuiDataGridControlColumn = { rowCellRender: SelectionRowCell, width: 100, }; + +export const mockRowAdditionalLeadingControls = ['visBarVerticalStacked', 'heart', 'inspect'].map( + (iconType, index): RowControlColumn => ({ + id: `exampleControl_${iconType}`, + headerAriaLabel: `Example Row Control ${iconType}`, + renderControl: (Control, rowProps) => { + return ( + <Control + data-test-subj={`exampleRowControl-${iconType}`} + label={`Example ${iconType}`} + iconType={iconType} + onClick={() => { + alert(`Example "${iconType}" control clicked. Row index: ${rowProps.rowIndex}`); + }} + /> + ); + }, + }) +); diff --git a/packages/kbn-unified-data-table/index.ts b/packages/kbn-unified-data-table/index.ts index 0929c33208fa0..7dace83c3774e 100644 --- a/packages/kbn-unified-data-table/index.ts +++ b/packages/kbn-unified-data-table/index.ts @@ -25,7 +25,7 @@ export { getRowsPerPageOptions } from './src/utils/rows_per_page'; export { popularizeField } from './src/utils/popularize_field'; export { useColumns } from './src/hooks/use_data_grid_columns'; -export { OPEN_DETAILS, SELECT_ROW } from './src/components/data_table_columns'; +export { OPEN_DETAILS, SELECT_ROW } from './src/components/data_table_columns'; // TODO: deprecate? export { DataTableRowControl } from './src/components/data_table_row_control'; export type { diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/get_additional_row_control_columns.test.tsx b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/get_additional_row_control_columns.test.tsx new file mode 100644 index 0000000000000..044b864213d82 --- /dev/null +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/get_additional_row_control_columns.test.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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 { getAdditionalRowControlColumns } from './get_additional_row_control_columns'; +import { mockRowAdditionalLeadingControls } from '../../../../__mocks__/external_control_columns'; + +describe('getAdditionalRowControlColumns', () => { + it('should work correctly for 0 controls', () => { + const columns = getAdditionalRowControlColumns([]); + + expect(columns).toHaveLength(0); + }); + + it('should work correctly for 1 control', () => { + const columns = getAdditionalRowControlColumns([mockRowAdditionalLeadingControls[0]]); + + expect(columns.map((column) => column.id)).toEqual([ + `additionalRowControl_${mockRowAdditionalLeadingControls[0].id}`, + ]); + }); + + it('should work correctly for 2 controls', () => { + const columns = getAdditionalRowControlColumns([ + mockRowAdditionalLeadingControls[0], + mockRowAdditionalLeadingControls[1], + ]); + + expect(columns.map((column) => column.id)).toEqual([ + `additionalRowControl_${mockRowAdditionalLeadingControls[0].id}`, + `additionalRowControl_${mockRowAdditionalLeadingControls[1].id}`, + ]); + }); + + it('should work correctly for 3 and more controls', () => { + const columns = getAdditionalRowControlColumns([ + mockRowAdditionalLeadingControls[0], + mockRowAdditionalLeadingControls[1], + mockRowAdditionalLeadingControls[2], + ]); + + expect(columns.map((column) => column.id)).toEqual([ + `additionalRowControl_${mockRowAdditionalLeadingControls[0].id}`, + `additionalRowControl_menuControl`, + ]); + }); +}); diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/get_additional_row_control_columns.ts b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/get_additional_row_control_columns.ts new file mode 100644 index 0000000000000..bd297b37bb5df --- /dev/null +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/get_additional_row_control_columns.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { EuiDataGridControlColumn } from '@elastic/eui'; +import type { RowControlColumn } from '../../../types'; +import { getRowControlColumn } from './row_control_column'; +import { getRowMenuControlColumn } from './row_menu_control_column'; + +export const getAdditionalRowControlColumns = ( + rowControlColumns: RowControlColumn[] +): EuiDataGridControlColumn[] => { + if (rowControlColumns.length <= 2) { + return rowControlColumns.map(getRowControlColumn); + } + + return [ + getRowControlColumn(rowControlColumns[0]), + getRowMenuControlColumn(rowControlColumns.slice(1)), + ]; +}; diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/index.ts b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/index.ts new file mode 100644 index 0000000000000..d3a79fc3a0d50 --- /dev/null +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/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 { getAdditionalRowControlColumns } from './get_additional_row_control_columns'; diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_control_column.test.tsx b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_control_column.test.tsx new file mode 100644 index 0000000000000..360fa7bc235c4 --- /dev/null +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_control_column.test.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 type { EuiDataGridCellValueElementProps } from '@elastic/eui'; +import { render, screen } from '@testing-library/react'; +import { getRowControlColumn } from './row_control_column'; +import { dataTableContextMock } from '../../../../__mocks__/table_context'; +import { UnifiedDataTableContext } from '../../../table_context'; + +describe('getRowControlColumn', () => { + const contextMock = { + ...dataTableContextMock, + }; + + it('should render the component', () => { + const mockClick = jest.fn(); + const props = { + id: 'test_row_control', + headerAriaLabel: 'row control', + renderControl: jest.fn((Control, rowProps) => ( + <Control label={`test-${rowProps.rowIndex}`} iconType="heart" onClick={mockClick} /> + )), + }; + const rowControlColumn = getRowControlColumn(props); + const RowControlColumn = + rowControlColumn.rowCellRender as React.FC<EuiDataGridCellValueElementProps>; + render( + <UnifiedDataTableContext.Provider value={contextMock}> + <RowControlColumn + rowIndex={1} + setCellProps={jest.fn()} + columnId={props.id} + colIndex={0} + isDetails={false} + isExpandable={false} + isExpanded={false} + /> + </UnifiedDataTableContext.Provider> + ); + const button = screen.getByTestId('unifiedDataTable_rowControl_test_row_control'); + expect(button).toBeInTheDocument(); + + button.click(); + + expect(mockClick).toHaveBeenCalledWith({ record: contextMock.rows[1], rowIndex: 1 }); + }); +}); diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_control_column.tsx b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_control_column.tsx new file mode 100644 index 0000000000000..f8d3ad063fb2d --- /dev/null +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_control_column.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, { useMemo } from 'react'; +import { + EuiButtonIcon, + EuiDataGridCellValueElementProps, + EuiDataGridControlColumn, + EuiScreenReaderOnly, + EuiToolTip, +} from '@elastic/eui'; +import { DataTableRowControl, Size } from '../../data_table_row_control'; +import type { RowControlColumn, RowControlProps } from '../../../types'; +import { DEFAULT_CONTROL_COLUMN_WIDTH } from '../../../constants'; +import { useControlColumn } from '../../../hooks/use_control_column'; + +export const RowControlCell = ({ + renderControl, + ...props +}: EuiDataGridCellValueElementProps & { + renderControl: RowControlColumn['renderControl']; +}) => { + const rowProps = useControlColumn(props); + + const Control: React.FC<RowControlProps> = useMemo( + () => + ({ 'data-test-subj': dataTestSubj, color, disabled, label, iconType, onClick }) => { + return ( + <DataTableRowControl size={Size.normal}> + <EuiToolTip content={label} delay="long"> + <EuiButtonIcon + data-test-subj={dataTestSubj ?? `unifiedDataTable_rowControl_${props.columnId}`} + disabled={disabled} + iconSize="s" + iconType={iconType} + color={color ?? 'text'} + aria-label={label} + onClick={() => { + onClick?.(rowProps); + }} + /> + </EuiToolTip> + </DataTableRowControl> + ); + }, + [props.columnId, rowProps] + ); + + return renderControl(Control, rowProps); +}; + +export const getRowControlColumn = ( + rowControlColumn: RowControlColumn +): EuiDataGridControlColumn => { + const { id, headerAriaLabel, headerCellRender, renderControl } = rowControlColumn; + + return { + id: `additionalRowControl_${id}`, + width: DEFAULT_CONTROL_COLUMN_WIDTH, + headerCellRender: + headerCellRender ?? + (() => ( + <EuiScreenReaderOnly> + <span>{headerAriaLabel}</span> + </EuiScreenReaderOnly> + )), + rowCellRender: (props) => { + return <RowControlCell {...props} renderControl={renderControl} />; + }, + }; +}; diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_menu_control_column.test.tsx b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_menu_control_column.test.tsx new file mode 100644 index 0000000000000..8e26e3f01d062 --- /dev/null +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_menu_control_column.test.tsx @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import type { EuiDataGridCellValueElementProps } from '@elastic/eui'; +import { render, screen } from '@testing-library/react'; +import { getRowMenuControlColumn } from './row_menu_control_column'; +import { dataTableContextMock } from '../../../../__mocks__/table_context'; +import { mockRowAdditionalLeadingControls } from '../../../../__mocks__/external_control_columns'; +import { UnifiedDataTableContext } from '../../../table_context'; + +describe('getRowMenuControlColumn', () => { + const contextMock = { + ...dataTableContextMock, + }; + + it('should render the component', () => { + const mockClick = jest.fn(); + const props = { + id: 'test_row_menu_control', + headerAriaLabel: 'row control', + renderControl: jest.fn((Control, rowProps) => ( + <Control label={`test-${rowProps.rowIndex}`} iconType="heart" onClick={mockClick} /> + )), + }; + const rowMenuControlColumn = getRowMenuControlColumn([ + props, + mockRowAdditionalLeadingControls[0], + mockRowAdditionalLeadingControls[1], + ]); + const RowMenuControlColumn = + rowMenuControlColumn.rowCellRender as React.FC<EuiDataGridCellValueElementProps>; + render( + <UnifiedDataTableContext.Provider value={contextMock}> + <RowMenuControlColumn + rowIndex={1} + setCellProps={jest.fn()} + columnId={props.id} + colIndex={0} + isDetails={false} + isExpandable={false} + isExpanded={false} + /> + </UnifiedDataTableContext.Provider> + ); + const menuButton = screen.getByTestId('unifiedDataTable_test_row_menu_control'); + expect(menuButton).toBeInTheDocument(); + + menuButton.click(); + + expect(screen.getByTestId('exampleRowControl-visBarVerticalStacked')).toBeInTheDocument(); + expect(screen.getByTestId('exampleRowControl-heart')).toBeInTheDocument(); + + const button = screen.getByTestId('unifiedDataTable_rowMenu_test_row_menu_control'); + expect(button).toBeInTheDocument(); + + button.click(); + expect(mockClick).toHaveBeenCalledWith({ record: contextMock.rows[1], rowIndex: 1 }); + }); +}); diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_menu_control_column.tsx b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_menu_control_column.tsx new file mode 100644 index 0000000000000..917174618fa37 --- /dev/null +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/additional_row_control/row_menu_control_column.tsx @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may 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, { Fragment, useCallback, useMemo, useState } from 'react'; +import { + EuiButtonIcon, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiDataGridCellValueElementProps, + EuiDataGridControlColumn, + EuiPopover, + EuiScreenReaderOnly, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { DataTableRowControl, Size } from '../../data_table_row_control'; +import type { RowControlColumn, RowControlProps } from '../../../types'; +import { DEFAULT_CONTROL_COLUMN_WIDTH } from '../../../constants'; +import { useControlColumn } from '../../../hooks/use_control_column'; + +/** + * Menu button under which all other additional row controls would be placed + */ +export const RowMenuControlCell = ({ + rowControlColumns, + ...props +}: EuiDataGridCellValueElementProps & { + rowControlColumns: RowControlColumn[]; +}) => { + const rowProps = useControlColumn(props); + const [isMoreActionsPopoverOpen, setIsMoreActionsPopoverOpen] = useState<boolean>(false); + + const buttonLabel = i18n.translate('unifiedDataTable.grid.additionalRowActions', { + defaultMessage: 'Additional actions', + }); + + const getControlComponent: (id: string) => React.FC<RowControlProps> = useCallback( + (id) => + ({ 'data-test-subj': dataTestSubj, color, disabled, label, iconType, onClick }) => { + return ( + <EuiContextMenuItem + data-test-subj={dataTestSubj ?? `unifiedDataTable_rowMenu_${id}`} + disabled={disabled} + icon={iconType} + color={color} + onClick={() => { + onClick?.(rowProps); + setIsMoreActionsPopoverOpen(false); + }} + > + {label} + </EuiContextMenuItem> + ); + }, + [rowProps, setIsMoreActionsPopoverOpen] + ); + + const popoverMenuItems = useMemo( + () => + rowControlColumns.map((rowControlColumn) => { + const Control = getControlComponent(rowControlColumn.id); + return ( + <Fragment key={rowControlColumn.id}> + {rowControlColumn.renderControl(Control, rowProps)} + </Fragment> + ); + }), + [rowControlColumns, rowProps, getControlComponent] + ); + + return ( + <EuiPopover + id={`rowMenuActionsPopover_${props.rowIndex}`} + button={ + <DataTableRowControl size={Size.normal}> + <EuiToolTip content={buttonLabel} delay="long"> + <EuiButtonIcon + data-test-subj={`unifiedDataTable_${props.columnId}`} + iconSize="s" + iconType="boxesVertical" + color="text" + aria-label={buttonLabel} + css={css` + .euiDataGridRowCell__content--defaultHeight & { + margin-top: 2px; // to align with other controls + } + `} + onClick={() => { + setIsMoreActionsPopoverOpen(!isMoreActionsPopoverOpen); + }} + /> + </EuiToolTip> + </DataTableRowControl> + } + isOpen={isMoreActionsPopoverOpen} + closePopover={() => setIsMoreActionsPopoverOpen(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + <EuiContextMenuPanel size="s" items={popoverMenuItems} /> + </EuiPopover> + ); +}; + +export const getRowMenuControlColumn = ( + rowControlColumns: RowControlColumn[] +): EuiDataGridControlColumn => { + return { + id: 'additionalRowControl_menuControl', + width: DEFAULT_CONTROL_COLUMN_WIDTH, + headerCellRender: () => ( + <EuiScreenReaderOnly> + <span> + {i18n.translate('unifiedDataTable.additionalActionsColumnHeader', { + defaultMessage: 'Additional actions column', + })} + </span> + </EuiScreenReaderOnly> + ), + rowCellRender: (props) => { + return <RowMenuControlCell {...props} rowControlColumns={rowControlColumns} />; + }, + }; +}; diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/color_indicator/color_indicator_control_column.tsx b/packages/kbn-unified-data-table/src/components/custom_control_columns/color_indicator/color_indicator_control_column.tsx index dd9be4ab90d13..902667810e613 100644 --- a/packages/kbn-unified-data-table/src/components/custom_control_columns/color_indicator/color_indicator_control_column.tsx +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/color_indicator/color_indicator_control_column.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useContext, useEffect } from 'react'; +import React from 'react'; import { css } from '@emotion/react'; import { EuiDataGridControlColumn, @@ -15,7 +15,7 @@ import { EuiDataGridCellValueElementProps, } from '@elastic/eui'; import type { DataTableRecord } from '@kbn/discover-utils'; -import { UnifiedDataTableContext } from '../../../table_context'; +import { useControlColumn } from '../../../hooks/use_control_column'; const COLOR_INDICATOR_WIDTH = 4; @@ -28,32 +28,14 @@ interface ColorIndicatorCellParams { ) => { color: string; label: string } | undefined; } -const ColorIndicatorCell: React.FC<ColorIndicatorCellParams> = ({ - rowIndex, - setCellProps, - getRowIndicator, -}) => { +const ColorIndicatorCell: React.FC<ColorIndicatorCellParams> = ({ getRowIndicator, ...props }) => { + const { record } = useControlColumn(props); const { euiTheme } = useEuiTheme(); - const { rows, expanded } = useContext(UnifiedDataTableContext); - const row = rows[rowIndex]; - const configuration = row ? getRowIndicator(row, euiTheme) : undefined; + + const configuration = record ? getRowIndicator(record, euiTheme) : undefined; const color = configuration?.color || 'transparent'; const label = configuration?.label; - useEffect(() => { - if (row.isAnchor) { - setCellProps({ - className: 'unifiedDataTable__cell--highlight', - }); - } else if (expanded && row && expanded.id === row.id) { - setCellProps({ - className: 'unifiedDataTable__cell--expanded', - }); - } else { - setCellProps({ className: '' }); - } - }, [expanded, row, setCellProps]); - return ( <div data-test-subj="unifiedDataTableRowColorIndicatorCell" diff --git a/packages/kbn-unified-data-table/src/components/custom_control_columns/index.ts b/packages/kbn-unified-data-table/src/components/custom_control_columns/index.ts index 8ff889b66d9e5..7e79f6e995f4f 100644 --- a/packages/kbn-unified-data-table/src/components/custom_control_columns/index.ts +++ b/packages/kbn-unified-data-table/src/components/custom_control_columns/index.ts @@ -10,3 +10,5 @@ export { getColorIndicatorControlColumn, type ColorIndicatorControlColumnParams, } from './color_indicator'; + +export { getAdditionalRowControlColumns } from './additional_row_control'; diff --git a/packages/kbn-unified-data-table/src/components/data_table.scss b/packages/kbn-unified-data-table/src/components/data_table.scss index 89707b5e888b2..f80b6cdab904c 100644 --- a/packages/kbn-unified-data-table/src/components/data_table.scss +++ b/packages/kbn-unified-data-table/src/components/data_table.scss @@ -55,9 +55,13 @@ .euiDataGridRowCell--controlColumn .euiDataGridRowCell__content, .euiDataGridRowCell.euiDataGridRowCell--controlColumn[data-gridcell-column-id='openDetails'], - .euiDataGridRowCell.euiDataGridRowCell--controlColumn[data-gridcell-column-id='select'] { + .euiDataGridRowCell.euiDataGridRowCell--controlColumn[data-gridcell-column-id='select'], + .euiDataGridRowCell.euiDataGridRowCell--controlColumn[data-gridcell-column-id^='additionalRowControl_'], + .euiDataGridHeaderCell.euiDataGridHeaderCell--controlColumn[data-gridcell-column-id^='additionalRowControl_'] { padding-left: 0; padding-right: 0; + border-left: 0; + border-right: 0; } .euiDataGridHeaderCell.euiDataGridHeaderCell--controlColumn[data-gridcell-column-id='select'] { @@ -137,6 +141,13 @@ .euiDataGridRowCell__content--defaultHeight & { // "Single line" row height setting margin-top: 0; } + + &--size-normal { + display: inline-block; + width: $euiSizeL; + height: $euiSizeL; + overflow: hidden; + } } .unifiedDataTable__descriptionList { 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 e469544857f49..6366aa4445630 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 @@ -26,6 +26,7 @@ import { buildDataTableRecord, getDocId } from '@kbn/discover-utils'; import type { DataTableRecord, EsHitRecord } from '@kbn/discover-utils/types'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { + mockRowAdditionalLeadingControls, testLeadingControlColumn, testTrailingControlColumns, } from '../../__mocks__/external_control_columns'; @@ -451,9 +452,8 @@ describe('UnifiedDataTable', () => { }); }); - describe('customControlColumnsConfiguration', () => { - const customControlColumnsConfiguration = jest.fn(); - it('should be able to customise the leading control column', async () => { + describe('custom control columns', () => { + it('should be able to customise the leading controls', async () => { const component = await getComponent({ ...getProps(), expandedDoc: { @@ -467,23 +467,19 @@ describe('UnifiedDataTable', () => { setExpandedDoc: jest.fn(), renderDocumentView: jest.fn(), externalControlColumns: [testLeadingControlColumn], - customControlColumnsConfiguration: customControlColumnsConfiguration.mockImplementation( - () => { - return { - leadingControlColumns: [testLeadingControlColumn, testTrailingControlColumns[0]], - trailingControlColumns: [], - }; - } - ), + rowAdditionalLeadingControls: mockRowAdditionalLeadingControls, }); expect(findTestSubject(component, 'test-body-control-column-cell').exists()).toBeTruthy(); expect( - findTestSubject(component, 'test-trailing-column-popover-button').exists() + findTestSubject(component, 'exampleRowControl-visBarVerticalStacked').exists() + ).toBeTruthy(); + expect( + findTestSubject(component, 'unifiedDataTable_additionalRowControl_menuControl').exists() ).toBeTruthy(); }); - it('should be able to customise the trailing control column', async () => { + it('should be able to customise the trailing controls', async () => { const component = await getComponent({ ...getProps(), expandedDoc: { @@ -497,14 +493,7 @@ describe('UnifiedDataTable', () => { setExpandedDoc: jest.fn(), renderDocumentView: jest.fn(), externalControlColumns: [testLeadingControlColumn], - customControlColumnsConfiguration: customControlColumnsConfiguration.mockImplementation( - () => { - return { - leadingControlColumns: [], - trailingControlColumns: [testLeadingControlColumn, testTrailingControlColumns[0]], - }; - } - ), + trailingControlColumns: testTrailingControlColumns, }); expect(findTestSubject(component, 'test-body-control-column-cell').exists()).toBeTruthy(); diff --git a/packages/kbn-unified-data-table/src/components/data_table.tsx b/packages/kbn-unified-data-table/src/components/data_table.tsx index 1a12151c7c18a..243b86b540865 100644 --- a/packages/kbn-unified-data-table/src/components/data_table.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table.tsx @@ -51,18 +51,19 @@ import { DataTableColumnsMeta, CustomCellRenderer, CustomGridColumnsConfiguration, - CustomControlColumnConfiguration, + RowControlColumn, } from '../types'; import { getDisplayedColumns } from '../utils/columns'; import { convertValueToString } from '../utils/convert_value_to_string'; import { getRowsPerPageOptions } from '../utils/rows_per_page'; import { getRenderCellValueFn } from '../utils/get_render_cell_value'; import { - getAllControlColumns, getEuiGridColumns, getLeadControlColumns, getVisibleColumns, canPrependTimeFieldColumn, + SELECT_ROW, + OPEN_DETAILS, } from './data_table_columns'; import { UnifiedDataTableContext } from '../table_context'; import { getSchemaDetectors } from './data_table_schema'; @@ -85,8 +86,11 @@ import { useSelectedDocs } from '../hooks/use_selected_docs'; import { getColorIndicatorControlColumn, type ColorIndicatorControlColumnParams, + getAdditionalRowControlColumns, } from './custom_control_columns'; +const CONTROL_COLUMN_IDS_DEFAULT = [SELECT_ROW, OPEN_DETAILS]; + export type SortOrder = [string, string]; export enum DataLoadingState { @@ -290,9 +294,20 @@ export interface UnifiedDataTableProps { */ maxDocFieldsDisplayed?: number; /** + * @deprecated Use only `rowAdditionalLeadingControls` instead * Optional value for providing EuiDataGridControlColumn list of the additional leading control columns. UnifiedDataTable includes two control columns: Open Details and Select. */ externalControlColumns?: EuiDataGridControlColumn[]; + /** + * An optional list of the EuiDataGridControlColumn type for setting trailing control columns standard for EuiDataGrid. + * We recommend to rather position all controls in the beginning of rows and use `rowAdditionalLeadingControls` for that + * as number of columns can be dynamically changed and we don't want the controls to become hidden due to horizontal scroll. + */ + trailingControlColumns?: EuiDataGridControlColumn[]; + /** + * Optional value to extend the list of default row actions + */ + rowAdditionalLeadingControls?: RowControlColumn[]; /** * Number total hits from ES */ @@ -327,10 +342,6 @@ export interface UnifiedDataTableProps { * @param gridProps */ renderCustomToolbar?: UnifiedDataTableRenderCustomToolbar; - /** - * An optional list of the EuiDataGridControlColumn type for setting trailing control columns standard for EuiDataGrid. - */ - trailingControlColumns?: EuiDataGridControlColumn[]; /** * An optional value for a custom number of the visible cell actions in the table. By default is up to 3. **/ @@ -347,10 +358,6 @@ export interface UnifiedDataTableProps { * An optional settings for customising the column */ customGridColumnsConfiguration?: CustomGridColumnsConfiguration; - /** - * An optional settings to control which columns to render as trailing and leading control columns - */ - customControlColumnsConfiguration?: CustomControlColumnConfiguration; /** * Name of the UnifiedDataTable consumer component or application */ @@ -396,8 +403,6 @@ export interface UnifiedDataTableProps { export const EuiDataGridMemoized = React.memo(EuiDataGrid); -const CONTROL_COLUMN_IDS_DEFAULT = ['openDetails', 'select']; - export const UnifiedDataTable = ({ ariaLabelledBy, columns, @@ -407,6 +412,7 @@ export const UnifiedDataTable = ({ headerRowHeightState, onUpdateHeaderRowHeight, controlColumnIds = CONTROL_COLUMN_IDS_DEFAULT, + rowAdditionalLeadingControls, dataView, loadingState, onFilter, @@ -437,7 +443,8 @@ export const UnifiedDataTable = ({ services, renderCustomGridBody, renderCustomToolbar, - trailingControlColumns, + externalControlColumns, // TODO: deprecate in favor of rowAdditionalLeadingControls + trailingControlColumns, // TODO: deprecate in favor of rowAdditionalLeadingControls totalHits, onFetchMoreRecords, renderDocumentView, @@ -446,7 +453,6 @@ export const UnifiedDataTable = ({ configRowHeight, showMultiFields = true, maxDocFieldsDisplayed = 50, - externalControlColumns, externalAdditionalControls, rowsPerPageOptions, visibleCellActions, @@ -458,7 +464,6 @@ export const UnifiedDataTable = ({ rowLineHeightOverride, cellActionsMetadata, customGridColumnsConfiguration, - customControlColumnsConfiguration, enableComparisonMode, cellContext, renderCellPopover, @@ -847,10 +852,19 @@ export const UnifiedDataTable = ({ const canSetExpandedDoc = Boolean(setExpandedDoc && !!renderDocumentView); const leadingControlColumns: EuiDataGridControlColumn[] = useMemo(() => { - const internalControlColumns = getLeadControlColumns(canSetExpandedDoc).filter(({ id }) => - controlColumnIds.includes(id) - ); - const leadingColumns = externalControlColumns + const defaultControlColumns = getLeadControlColumns(canSetExpandedDoc); + const internalControlColumns = controlColumnIds + ? // reorder the default controls as per controlColumnIds + controlColumnIds.reduce((acc, id) => { + const controlColumn = defaultControlColumns.find((col) => col.id === id); + if (controlColumn) { + acc.push(controlColumn); + } + return acc; + }, [] as EuiDataGridControlColumn[]) + : defaultControlColumns; + + const leadingColumns: EuiDataGridControlColumn[] = externalControlColumns ? [...internalControlColumns, ...externalControlColumns] : internalControlColumns; @@ -861,17 +875,18 @@ export const UnifiedDataTable = ({ leadingColumns.unshift(colorIndicatorControlColumn); } - return leadingColumns; - }, [canSetExpandedDoc, controlColumnIds, externalControlColumns, getRowIndicator]); - - const controlColumnsConfig = customControlColumnsConfiguration?.({ - controlColumns: getAllControlColumns(), - }); + if (rowAdditionalLeadingControls?.length) { + leadingColumns.push(...getAdditionalRowControlColumns(rowAdditionalLeadingControls)); + } - const customLeadingControlColumn = - controlColumnsConfig?.leadingControlColumns ?? leadingControlColumns; - const customTrailingControlColumn = - controlColumnsConfig?.trailingControlColumns ?? trailingControlColumns; + return leadingColumns; + }, [ + canSetExpandedDoc, + controlColumnIds, + externalControlColumns, + getRowIndicator, + rowAdditionalLeadingControls, + ]); const additionalControls = useMemo(() => { if (!externalAdditionalControls && !selectedDocIds.length) { @@ -1082,7 +1097,7 @@ export const UnifiedDataTable = ({ columns={euiGridColumns} columnVisibility={columnsVisibility} data-test-subj="docTable" - leadingControlColumns={customLeadingControlColumn} + leadingControlColumns={leadingControlColumns} onColumnResize={onResize} pagination={paginationObj} renderCellValue={renderCellValue} @@ -1096,7 +1111,7 @@ export const UnifiedDataTable = ({ gridStyle={gridStyleOverride ?? GRID_STYLE} renderCustomGridBody={renderCustomGridBody} renderCustomToolbar={renderCustomToolbarFn} - trailingControlColumns={customTrailingControlColumn} + trailingControlColumns={trailingControlColumns} cellContext={cellContext} renderCellPopover={renderCustomPopover} /> diff --git a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx index 202a593018792..4528e323c2047 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx @@ -17,12 +17,16 @@ import { type DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { ToastsStart, IUiSettingsClient } from '@kbn/core/public'; import { DocViewFilterFn } from '@kbn/unified-doc-viewer/types'; import { ExpandButton } from './data_table_expand_button'; -import { ControlColumns, CustomGridColumnsConfiguration, UnifiedDataTableSettings } from '../types'; +import { CustomGridColumnsConfiguration, UnifiedDataTableSettings } from '../types'; import type { ValueToStringConverter, DataTableColumnsMeta } from '../types'; import { buildCellActions } from './default_cell_actions'; import { getSchemaByKbnType } from './data_table_schema'; import { SelectButton, SelectAllButton } from './data_table_document_selection'; -import { defaultTimeColumnWidth, ROWS_HEIGHT_OPTIONS } from '../constants'; +import { + defaultTimeColumnWidth, + ROWS_HEIGHT_OPTIONS, + DEFAULT_CONTROL_COLUMN_WIDTH, +} from '../constants'; import { buildCopyColumnNameButton, buildCopyColumnValuesButton } from './build_copy_column_button'; import { buildEditFieldButton } from './build_edit_field_button'; import { DataTableColumnHeader, DataTableTimeColumnHeader } from './data_table_column_header'; @@ -53,7 +57,7 @@ export const SELECT_ROW = 'select'; const openDetails = { id: OPEN_DETAILS, - width: 26, + width: DEFAULT_CONTROL_COLUMN_WIDTH, headerCellRender: () => ( <EuiScreenReaderOnly> <span> @@ -68,18 +72,11 @@ const openDetails = { const select = { id: SELECT_ROW, - width: 24, + width: DEFAULT_CONTROL_COLUMN_WIDTH, rowCellRender: SelectButton, headerCellRender: SelectAllButton, }; -export function getAllControlColumns(): ControlColumns { - return { - [SELECT_ROW]: select, - [OPEN_DETAILS]: openDetails, - }; -} - export function getLeadControlColumns(canSetExpandedDoc: boolean) { if (!canSetExpandedDoc) { return [select]; diff --git a/packages/kbn-unified-data-table/src/components/data_table_document_selection.tsx b/packages/kbn-unified-data-table/src/components/data_table_document_selection.tsx index b844a4bb537ca..2a34c4866cb86 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_document_selection.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_document_selection.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useMemo, useState } from 'react'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { @@ -28,28 +28,19 @@ import { css } from '@emotion/react'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import type { UseSelectedDocsState } from '../hooks/use_selected_docs'; import { UnifiedDataTableContext } from '../table_context'; +import { useControlColumn } from '../hooks/use_control_column'; -export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueElementProps) => { +export const SelectButton = (props: EuiDataGridCellValueElementProps) => { + const { record, rowIndex } = useControlColumn(props); const { euiTheme } = useEuiTheme(); - const { selectedDocsState, expanded, rows, isDarkMode } = useContext(UnifiedDataTableContext); + const { selectedDocsState } = useContext(UnifiedDataTableContext); const { isDocSelected, toggleDocSelection } = selectedDocsState; - const doc = useMemo(() => rows[rowIndex], [rows, rowIndex]); const toggleDocumentSelectionLabel = i18n.translate('unifiedDataTable.grid.selectDoc', { defaultMessage: `Select document ''{rowNumber}''`, values: { rowNumber: rowIndex + 1 }, }); - useEffect(() => { - if (expanded && doc && expanded.id === doc.id) { - setCellProps({ - className: 'unifiedDataTable__cell--selected', - }); - } else { - setCellProps({ className: '' }); - } - }, [expanded, doc, setCellProps, isDarkMode]); - return ( <EuiFlexGroup responsive={false} @@ -63,12 +54,12 @@ export const SelectButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle > <EuiFlexItem grow={false}> <EuiCheckbox - id={doc.id} + id={record.id} aria-label={toggleDocumentSelectionLabel} - checked={isDocSelected(doc.id)} - data-test-subj={`dscGridSelectDoc-${doc.id}`} + checked={isDocSelected(record.id)} + data-test-subj={`dscGridSelectDoc-${record.id}`} onChange={() => { - toggleDocSelection(doc.id); + toggleDocSelection(record.id); }} /> </EuiFlexItem> diff --git a/packages/kbn-unified-data-table/src/components/data_table_expand_button.tsx b/packages/kbn-unified-data-table/src/components/data_table_expand_button.tsx index da3c8a5026ea0..04ae49abec141 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_expand_button.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_expand_button.tsx @@ -10,39 +10,27 @@ import React, { useContext, useEffect, useRef, useState } from 'react'; import { EuiButtonIcon, EuiDataGridCellValueElementProps, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { UnifiedDataTableContext } from '../table_context'; -import { DataTableRowControl } from './data_table_row_control'; +import { DataTableRowControl, Size } from './data_table_row_control'; +import { useControlColumn } from '../hooks/use_control_column'; /** * Button to expand a given row */ -export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueElementProps) => { +export const ExpandButton = (props: EuiDataGridCellValueElementProps) => { + const { record, rowIndex } = useControlColumn(props); + const toolTipRef = useRef<EuiToolTip>(null); const [pressed, setPressed] = useState<boolean>(false); - const { expanded, setExpanded, rows, isDarkMode, componentsTourSteps } = - useContext(UnifiedDataTableContext); - const current = rows[rowIndex]; + const { expanded, setExpanded, componentsTourSteps } = useContext(UnifiedDataTableContext); const tourStep = componentsTourSteps ? componentsTourSteps.expandButton : undefined; - useEffect(() => { - if (current.isAnchor) { - setCellProps({ - className: 'unifiedDataTable__cell--highlight', - }); - } else if (expanded && current && expanded.id === current.id) { - setCellProps({ - className: 'unifiedDataTable__cell--expanded', - }); - } else { - setCellProps({ className: '' }); - } - }, [expanded, current, setCellProps, isDarkMode]); - const isCurrentRowExpanded = current === expanded; + const isCurrentRowExpanded = record === expanded; const buttonLabel = i18n.translate('unifiedDataTable.grid.viewDoc', { defaultMessage: 'Toggle dialog with details', }); - const testSubj = current.isAnchor + const testSubj = record.isAnchor ? 'docTableExpandToggleColumnAnchor' : 'docTableExpandToggleColumn'; @@ -60,7 +48,7 @@ export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle } return ( - <DataTableRowControl> + <DataTableRowControl size={Size.normal}> <EuiToolTip content={buttonLabel} delay="long" ref={toolTipRef}> <EuiButtonIcon id={rowIndex === 0 ? tourStep : undefined} @@ -69,7 +57,7 @@ export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueEle aria-label={buttonLabel} data-test-subj={testSubj} onClick={() => { - const nextHit = isCurrentRowExpanded ? undefined : current; + const nextHit = isCurrentRowExpanded ? undefined : record; toolTipRef.current?.hideToolTip(); setPressed(Boolean(nextHit)); setExpanded?.(nextHit); diff --git a/packages/kbn-unified-data-table/src/components/data_table_row_control.tsx b/packages/kbn-unified-data-table/src/components/data_table_row_control.tsx index 4ceadea549dce..0ac0bbd4cb2f6 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_row_control.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_row_control.tsx @@ -7,7 +7,16 @@ */ import React from 'react'; +import classnames from 'classnames'; -export const DataTableRowControl = ({ children }: { children: React.ReactNode }) => { - return <span className="unifiedDataTable__rowControl">{children}</span>; +export enum Size { + normal = 'normal', +} + +export const DataTableRowControl: React.FC<{ size?: Size }> = ({ size, children }) => { + const classes = classnames('unifiedDataTable__rowControl', { + // normalize the size of the control + [`unifiedDataTable__rowControl--size-${size}`]: size, + }); + return <span className={classes}>{children}</span>; }; diff --git a/packages/kbn-unified-data-table/src/constants.ts b/packages/kbn-unified-data-table/src/constants.ts index c2d5654c602c2..c7cf1793039a5 100644 --- a/packages/kbn-unified-data-table/src/constants.ts +++ b/packages/kbn-unified-data-table/src/constants.ts @@ -7,6 +7,8 @@ */ import { EuiDataGridStyle } from '@elastic/eui'; +export const DEFAULT_CONTROL_COLUMN_WIDTH = 24; + export const DEFAULT_ROWS_PER_PAGE = 100; export const MAX_LOADED_GRID_ROWS = 10000; diff --git a/packages/kbn-unified-data-table/src/hooks/use_control_column.ts b/packages/kbn-unified-data-table/src/hooks/use_control_column.ts new file mode 100644 index 0000000000000..e2bc05f668508 --- /dev/null +++ b/packages/kbn-unified-data-table/src/hooks/use_control_column.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useContext, useEffect, useMemo } from 'react'; +import type { EuiDataGridCellValueElementProps } from '@elastic/eui'; +import type { DataTableRecord } from '@kbn/discover-utils'; +import { UnifiedDataTableContext } from '../table_context'; + +export const useControlColumn = ({ + rowIndex, + setCellProps, +}: Pick<EuiDataGridCellValueElementProps, 'rowIndex' | 'setCellProps'>): { + record: DataTableRecord; + rowIndex: number; +} => { + const { expanded, rows } = useContext(UnifiedDataTableContext); + const record = useMemo(() => rows[rowIndex], [rows, rowIndex]); + + useEffect(() => { + if (record.isAnchor) { + setCellProps({ + className: 'unifiedDataTable__cell--highlight', + }); + } else if (expanded && record && expanded.id === record.id) { + setCellProps({ + className: 'unifiedDataTable__cell--expanded', + }); + } else { + setCellProps({ + className: '', + }); + } + }, [expanded, record, setCellProps]); + + return useMemo(() => ({ record, rowIndex }), [record, rowIndex]); +}; diff --git a/packages/kbn-unified-data-table/src/types.ts b/packages/kbn-unified-data-table/src/types.ts index 5914fa03f8827..b08818e1a861a 100644 --- a/packages/kbn-unified-data-table/src/types.ts +++ b/packages/kbn-unified-data-table/src/types.ts @@ -6,8 +6,13 @@ * Side Public License, v 1. */ -import type { ReactElement } from 'react'; -import type { EuiDataGridCellValueElementProps, EuiDataGridColumn } from '@elastic/eui'; +import type { ReactElement, FC } from 'react'; +import type { + EuiDataGridCellValueElementProps, + EuiDataGridColumn, + IconType, + EuiButtonIconProps, +} from '@elastic/eui'; import type { DataTableRecord } from '@kbn/discover-utils/src/types'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; @@ -70,16 +75,25 @@ export type CustomGridColumnsConfiguration = Record< (props: CustomGridColumnProps) => EuiDataGridColumn >; -export interface ControlColumns { - select: EuiDataGridControlColumn; - openDetails: EuiDataGridControlColumn; +export interface RowControlRowProps { + rowIndex: number; + record: DataTableRecord; } -export interface ControlColumnsProps { - controlColumns: ControlColumns; +export interface RowControlProps { + 'data-test-subj'?: string; + color?: EuiButtonIconProps['color']; + disabled?: boolean; + label: string; + iconType: IconType; + onClick: ((props: RowControlRowProps) => void) | undefined; } -export type CustomControlColumnConfiguration = (props: ControlColumnsProps) => { - leadingControlColumns: EuiDataGridControlColumn[]; - trailingControlColumns?: EuiDataGridControlColumn[]; -}; +export type RowControlComponent = FC<RowControlProps>; + +export interface RowControlColumn { + id: string; + headerAriaLabel: string; + headerCellRender?: EuiDataGridControlColumn['headerCellRender']; + renderControl: (Control: RowControlComponent, props: RowControlRowProps) => ReactElement; +} diff --git a/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar_container.tsx b/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar_container.tsx index 68b3380b712ad..2b2aa90427a8d 100644 --- a/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar_container.tsx +++ b/packages/kbn-unified-field-list/src/containers/unified_field_list_sidebar/field_list_sidebar_container.tsx @@ -193,8 +193,8 @@ const UnifiedFieldListSidebarContainer = memo( const deleteField = useMemo( () => dataView && dataViewFieldEditor && editField - ? (fieldName: string) => { - const ref = dataViewFieldEditor.openDeleteModal({ + ? async (fieldName: string) => { + const ref = await dataViewFieldEditor.openDeleteModal({ ctx: { dataView, }, diff --git a/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts b/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts index 9f98aa798d6c7..d816ce835fa51 100644 --- a/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts +++ b/packages/presentation/presentation_containers/interfaces/unsaved_changes/children_unsaved_changes.test.ts @@ -6,12 +6,11 @@ * Side Public License, v 1. */ -import { BehaviorSubject, skip } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { childrenUnsavedChanges$, DEBOUNCE_TIME } from './children_unsaved_changes'; +import { waitFor } from '@testing-library/react'; -// Failing: See https://github.com/elastic/kibana/issues/189823 -// FLAKY: https://github.com/elastic/kibana/issues/189822 -describe.skip('childrenUnsavedChanges$', () => { +describe('childrenUnsavedChanges$', () => { const child1Api = { unsavedChanges: new BehaviorSubject<object | undefined>(undefined), resetUnsavedChanges: () => undefined, @@ -35,40 +34,64 @@ describe.skip('childrenUnsavedChanges$', () => { test('should emit on subscribe', async () => { const subscription = childrenUnsavedChanges$(children$).subscribe(onFireMock); - await new Promise((resolve) => setTimeout(resolve, DEBOUNCE_TIME + 1)); - expect(onFireMock).toHaveBeenCalledTimes(1); - const childUnsavedChanges = onFireMock.mock.calls[0][0]; - expect(childUnsavedChanges).toBeUndefined(); + await waitFor( + () => { + expect(onFireMock).toHaveBeenCalledTimes(1); + const childUnsavedChanges = onFireMock.mock.calls[0][0]; + expect(childUnsavedChanges).toBeUndefined(); + }, + { + interval: DEBOUNCE_TIME + 1, + } + ); subscription.unsubscribe(); }); test('should emit when child has new unsaved changes', async () => { - const subscription = childrenUnsavedChanges$(children$).pipe(skip(1)).subscribe(onFireMock); - await new Promise((resolve) => setTimeout(resolve, DEBOUNCE_TIME + 1)); - expect(onFireMock).toHaveBeenCalledTimes(0); + const subscription = childrenUnsavedChanges$(children$).subscribe(onFireMock); + await waitFor( + () => { + expect(onFireMock).toHaveBeenCalledTimes(1); + }, + { + interval: DEBOUNCE_TIME + 1, + } + ); child1Api.unsavedChanges.next({ key1: 'modified value', }); - await new Promise((resolve) => setTimeout(resolve, DEBOUNCE_TIME + 1)); - expect(onFireMock).toHaveBeenCalledTimes(1); - const childUnsavedChanges = onFireMock.mock.calls[0][0]; - expect(childUnsavedChanges).toEqual({ - child1: { - key1: 'modified value', + await waitFor( + () => { + expect(onFireMock).toHaveBeenCalledTimes(2); + const childUnsavedChanges = onFireMock.mock.calls[1][0]; + expect(childUnsavedChanges).toEqual({ + child1: { + key1: 'modified value', + }, + }); }, - }); + { + interval: DEBOUNCE_TIME + 1, + } + ); subscription.unsubscribe(); }); test('should emit when children changes', async () => { - const subscription = childrenUnsavedChanges$(children$).pipe(skip(1)).subscribe(onFireMock); - await new Promise((resolve) => setTimeout(resolve, DEBOUNCE_TIME + 1)); - expect(onFireMock).toHaveBeenCalledTimes(0); + const subscription = childrenUnsavedChanges$(children$).subscribe(onFireMock); + await waitFor( + () => { + expect(onFireMock).toHaveBeenCalledTimes(1); + }, + { + interval: DEBOUNCE_TIME + 1, + } + ); // add child children$.next({ @@ -78,15 +101,21 @@ describe.skip('childrenUnsavedChanges$', () => { resetUnsavedChanges: () => undefined, }, }); - await new Promise((resolve) => setTimeout(resolve, DEBOUNCE_TIME + 1)); - expect(onFireMock).toHaveBeenCalledTimes(1); - const childUnsavedChanges = onFireMock.mock.calls[0][0]; - expect(childUnsavedChanges).toEqual({ - child3: { - key1: 'modified value', + await waitFor( + () => { + expect(onFireMock).toHaveBeenCalledTimes(2); + const childUnsavedChanges = onFireMock.mock.calls[1][0]; + expect(childUnsavedChanges).toEqual({ + child3: { + key1: 'modified value', + }, + }); }, - }); + { + interval: DEBOUNCE_TIME + 1, + } + ); subscription.unsubscribe(); }); diff --git a/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts b/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts index f8a72e6add588..2a8f5a625d42a 100644 --- a/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts +++ b/packages/presentation/presentation_containers/interfaces/unsaved_changes/initialize_unsaved_changes.test.ts @@ -12,14 +12,14 @@ import { initializeUnsavedChanges, } from './initialize_unsaved_changes'; import { PublishesUnsavedChanges, StateComparators } from '@kbn/presentation-publishing'; +import { waitFor } from '@testing-library/react'; interface TestState { key1: string; key2: string; } -// Failing: See https://github.com/elastic/kibana/issues/189811 -describe.skip('unsavedChanges api', () => { +describe('unsavedChanges api', () => { const lastSavedState = { key1: 'original key1 value', key2: 'original key2 value', @@ -47,33 +47,42 @@ describe.skip('unsavedChanges api', () => { test('should have unsaved changes when state changes', async () => { key1$.next('modified key1 value'); - await new Promise((resolve) => setTimeout(resolve, COMPARATOR_SUBJECTS_DEBOUNCE + 1)); - expect(api?.unsavedChanges.value).toEqual({ - key1: 'modified key1 value', - }); + await waitFor( + () => + expect(api?.unsavedChanges.value).toEqual({ + key1: 'modified key1 value', + }), + { + interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, + } + ); }); test('should have no unsaved changes after save', async () => { key1$.next('modified key1 value'); - await new Promise((resolve) => setTimeout(resolve, COMPARATOR_SUBJECTS_DEBOUNCE + 1)); - expect(api?.unsavedChanges.value).not.toBeUndefined(); + await waitFor(() => expect(api?.unsavedChanges.value).not.toBeUndefined(), { + interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, + }); // trigger save parentApi.saveNotification$.next(); - await new Promise((resolve) => setTimeout(resolve, 0)); - expect(api?.unsavedChanges.value).toBeUndefined(); + await waitFor(() => expect(api?.unsavedChanges.value).toBeUndefined(), { + interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, + }); }); test('should have no unsaved changes after reset', async () => { key1$.next('modified key1 value'); - await new Promise((resolve) => setTimeout(resolve, COMPARATOR_SUBJECTS_DEBOUNCE + 1)); - expect(api?.unsavedChanges.value).not.toBeUndefined(); + await waitFor(() => expect(api?.unsavedChanges.value).not.toBeUndefined(), { + interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, + }); // trigger reset api?.resetUnsavedChanges(); - await new Promise((resolve) => setTimeout(resolve, COMPARATOR_SUBJECTS_DEBOUNCE + 1)); - expect(api?.unsavedChanges.value).toBeUndefined(); + await waitFor(() => expect(api?.unsavedChanges.value).toBeUndefined(), { + interval: COMPARATOR_SUBJECTS_DEBOUNCE + 1, + }); }); }); diff --git a/packages/serverless/settings/observability_project/index.ts b/packages/serverless/settings/observability_project/index.ts index 00d9d7b6d544f..07a9c84e210ff 100644 --- a/packages/serverless/settings/observability_project/index.ts +++ b/packages/serverless/settings/observability_project/index.ts @@ -33,4 +33,8 @@ export const OBSERVABILITY_PROJECT_SETTINGS = [ settings.OBSERVABILITY_APM_ENABLE_TABLE_SEARCH_BAR, settings.OBSERVABILITY_APM_ENABLE_SERVICE_INVENTORY_TABLE_SEARCH_BAR, settings.OBSERVABILITY_ENTITY_CENTRIC_EXPERIENCE, + settings.OBSERVABILITY_AI_ASSISTANT_LOGS_INDEX_PATTERN_ID, + settings.OBSERVABILITY_AI_ASSISTANT_RESPONSE_LANGUAGE, + settings.OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING, + settings.OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN, ]; diff --git a/packages/shared-ux/error_boundary/src/services/error_boundary_services.test.tsx b/packages/shared-ux/error_boundary/src/services/error_boundary_services.test.tsx index 8e8838e710e1f..c1842aff3baef 100644 --- a/packages/shared-ux/error_boundary/src/services/error_boundary_services.test.tsx +++ b/packages/shared-ux/error_boundary/src/services/error_boundary_services.test.tsx @@ -17,6 +17,7 @@ import { BadComponent } from '../../mocks'; describe('<KibanaErrorBoundaryProvider>', () => { let analytics: KibanaErrorBoundaryProviderDeps['analytics']; beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(() => {}); analytics = analyticsServiceMock.createAnalyticsServiceStart(); }); diff --git a/packages/shared-ux/error_boundary/src/services/error_service.test.ts b/packages/shared-ux/error_boundary/src/services/error_service.test.ts index e89ff74af220f..4f72e997a41e4 100644 --- a/packages/shared-ux/error_boundary/src/services/error_service.test.ts +++ b/packages/shared-ux/error_boundary/src/services/error_service.test.ts @@ -9,6 +9,10 @@ import { KibanaErrorService } from './error_service'; describe('KibanaErrorBoundary Error Service', () => { + beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + const mockDeps = { analytics: { reportEvent: jest.fn() }, }; diff --git a/packages/shared-ux/error_boundary/src/ui/error_boundary.test.tsx b/packages/shared-ux/error_boundary/src/ui/error_boundary.test.tsx index 472aeba92ad5a..cc95b2978aeca 100644 --- a/packages/shared-ux/error_boundary/src/ui/error_boundary.test.tsx +++ b/packages/shared-ux/error_boundary/src/ui/error_boundary.test.tsx @@ -19,6 +19,7 @@ import { errorMessageStrings as strings } from './message_strings'; describe('<KibanaErrorBoundary>', () => { let services: KibanaErrorBoundaryServices; beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(() => {}); services = getServicesMock(); }); diff --git a/packages/shared-ux/error_boundary/src/ui/error_boundary.tsx b/packages/shared-ux/error_boundary/src/ui/error_boundary.tsx index 8e38a9e172f13..7fd601f65cfc1 100644 --- a/packages/shared-ux/error_boundary/src/ui/error_boundary.tsx +++ b/packages/shared-ux/error_boundary/src/ui/error_boundary.tsx @@ -42,6 +42,9 @@ class ErrorBoundaryInternal extends React.Component< } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error('Error caught by Kibana React Error Boundary'); // eslint-disable-line no-console + console.error(error); // eslint-disable-line no-console + const { name, isFatal } = this.props.services.errorService.registerError(error, errorInfo); this.setState(() => { return { error, errorInfo, componentName: name, isFatal }; diff --git a/renovate.json b/renovate.json index bc94410bdea46..d45879aaa8816 100644 --- a/renovate.json +++ b/renovate.json @@ -1,8 +1,8 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:recommended"], + "extends": ["config:recommended", "helpers:pinGitHubActionDigests"], "ignorePaths": ["**/__fixtures__/**", "**/fixtures/**"], - "enabledManagers": ["npm"], + "enabledManagers": ["npm", "github-actions"], "baseBranches": ["main", "7.17"], "prConcurrentLimit": 0, "prHourlyLimit": 0, @@ -20,6 +20,14 @@ "matchDepPatterns": [".*"], "enabled": false }, + { + "groupName": "GitHub actions", + "matchManagers": ["github-actions"], + "reviewers": "[team:kibana-operations]", + "matchBaseBranches": ["main"], + "labels": ["Team:Operations", "backport:all-open", "release_note:skip"], + "enabled": true + }, { "groupName": "@elastic/charts", "matchDepNames": ["@elastic/charts"], diff --git a/sonar-project.properties b/sonar-project.properties index 3cac455d961f4..ed771429e6f76 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -4,7 +4,6 @@ sonar.host.url=https://sonar.elastic.dev sonar.sources=\ packages, \ - plugins, \ src, \ x-pack/packages, \ x-pack/plugins @@ -13,6 +12,8 @@ sonar.exclusions=\ **/*.mocks.*, \ **/*.spec.*, \ **/*.stories.js, \ + **/*.md, \ + **/*.mdx, \ **/*.stories.ts, \ **/*.story.js, \ **/*.story.ts, \ @@ -37,7 +38,6 @@ sonar.exclusions=\ **/mock_responses/**/*, \ **/mocks/**/*, \ **/node_modules/**/*, \ - **/packages/**/*, \ **/public/**/*, \ **/scripts/**/*, \ **/storybook/**/*, \ 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 02ad163c43931..90f020a42e278 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 @@ -86,6 +86,7 @@ describe('checking migration metadata changes on all registered SO types', () => "core-usage-stats": "b3c04da317c957741ebcdedfea4524049fdc79ff", "csp-rule-template": "c151324d5f85178169395eecb12bac6b96064654", "dashboard": "211e9ca30f5a95d5f3c27b1bf2b58e6cfa0c9ae9", + "dynamic-config-overrides": "eb3ec7d96a42991068eda5421eecba9349c82d2b", "endpoint:unified-user-artifact-manifest": "71c7fcb52c658b21ea2800a6b6a76972ae1c776e", "endpoint:user-artifact-manifest": "1c3533161811a58772e30cdc77bac4631da3ef2b", "enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d", diff --git a/src/core/server/integration_tests/config/check_dynamic_config.test.ts b/src/core/server/integration_tests/config/check_dynamic_config.test.ts index 7239f051f41e7..335b688d5b2f4 100644 --- a/src/core/server/integration_tests/config/check_dynamic_config.test.ts +++ b/src/core/server/integration_tests/config/check_dynamic_config.test.ts @@ -7,11 +7,73 @@ */ import { set } from '@kbn/safer-lodash-set'; -import { Root } from '@kbn/core-root-server-internal'; -import { createRootWithCorePlugins } from '@kbn/core-test-helpers-kbn-server'; +import type { Root } from '@kbn/core-root-server-internal'; +import { + createTestServers, + createRootWithCorePlugins, + type TestElasticsearchUtils, + request, +} from '@kbn/core-test-helpers-kbn-server'; import { PLUGIN_SYSTEM_ENABLE_ALL_PLUGINS_CONFIG_PATH } from '@kbn/core-plugins-server-internal/src/constants'; -describe('checking migration metadata changes on all registered SO types', () => { +describe('PUT /internal/core/_settings', () => { + let esServer: TestElasticsearchUtils; + let root: Root; + + const loggerName = 'my-test-logger'; + + beforeAll(async () => { + const settings = { + coreApp: { allowDynamicConfigOverrides: true }, + logging: { + loggers: [{ name: loggerName, level: 'error', appenders: ['console'] }], + }, + }; + const { startES, startKibana } = createTestServers({ + adjustTimeout: (t: number) => jest.setTimeout(t), + settings: { + kbn: settings, + }, + }); + + esServer = await startES(); + + const kbnUtils = await startKibana(); + root = kbnUtils.root; + + // eslint-disable-next-line dot-notation + root['server'].configService.addDynamicConfigPaths('logging', ['loggers']); // just for the sake of being able to change something easy to test + }); + + afterAll(async () => { + await root?.shutdown(); + await esServer?.stop(); + }); + + test('should update the log level', async () => { + const logger = root.logger.get(loggerName); + expect(logger.isLevelEnabled('info')).toBe(false); + await request + .put(root, '/internal/core/_settings') + .set('Elastic-Api-Version', '1') + .send({ 'logging.loggers': [{ name: loggerName, level: 'debug', appenders: ['console'] }] }) + .expect(200); + expect(logger.isLevelEnabled('info')).toBe(true); + }); + + test('should remove the setting', async () => { + const logger = root.logger.get(loggerName); + expect(logger.isLevelEnabled('info')).toBe(true); // still true from the previous test + await request + .put(root, '/internal/core/_settings') + .set('Elastic-Api-Version', '1') + .send({ 'logging.loggers': null }) + .expect(200); + expect(logger.isLevelEnabled('info')).toBe(false); + }); +}); + +describe('checking all opted-in dynamic config settings', () => { let root: Root; beforeAll(async () => { diff --git a/src/core/server/integration_tests/elasticsearch/version_compatibility.test.ts b/src/core/server/integration_tests/elasticsearch/version_compatibility.test.ts index bfd7575d58283..eb6cc767d5817 100644 --- a/src/core/server/integration_tests/elasticsearch/version_compatibility.test.ts +++ b/src/core/server/integration_tests/elasticsearch/version_compatibility.test.ts @@ -91,8 +91,7 @@ describe('Version Compatibility', () => { await expect(startServers({ customKibanaVersion: previousMinor() })).resolves.toBeUndefined(); }); - // FLAKY: https://github.com/elastic/kibana/issues/171289 - it.skip('should flag the incompatibility on version mismatch (ES is previous minor)', async () => { + it('should flag the incompatibility on version mismatch (ES is previous minor)', async () => { const found$ = new Subject<void>(); consoleSpy.mockImplementation((str) => { if (str.includes('is incompatible')) { 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 eebeaf9673973..6235f95147639 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 @@ -47,6 +47,7 @@ const previouslyRegisteredTypes = [ 'csp-rule-template', 'csp_rule', 'dashboard', + 'dynamic-config-overrides', // Added in 8.16 to persist the dynamic config overrides and share it with other nodes 'event-annotation-group', 'endpoint:user-artifact', 'endpoint:user-artifact-manifest', diff --git a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts index 052d7592024d7..1a3708a3d5bcd 100644 --- a/src/dev/build/tasks/os_packages/create_os_package_tasks.ts +++ b/src/dev/build/tasks/os_packages/create_os_package_tasks.ts @@ -111,7 +111,7 @@ export const CreateDockerServerless: Task = { async run(config, log, build) { await runDockerGenerator(config, log, build, { architecture: 'x64', - baseImage: 'ubuntu', + baseImage: 'wolfi', context: false, serverless: true, image: true, @@ -119,7 +119,7 @@ export const CreateDockerServerless: Task = { }); await runDockerGenerator(config, log, build, { architecture: 'aarch64', - baseImage: 'ubuntu', + baseImage: 'wolfi', context: false, serverless: true, image: true, @@ -210,7 +210,7 @@ export const CreateDockerContexts: Task = { image: false, }); await runDockerGenerator(config, log, build, { - baseImage: 'ubuntu', + baseImage: 'wolfi', serverless: true, context: true, image: false, diff --git a/src/dev/build/tasks/os_packages/docker_generator/run.ts b/src/dev/build/tasks/os_packages/docker_generator/run.ts index 62d17f0455723..6fac9dedfa387 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/run.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/run.ts @@ -44,11 +44,11 @@ export async function runDockerGenerator( if (flags.baseImage === 'ubi') baseImageName = 'docker.elastic.co/ubi9/ubi-minimal:latest'; if (flags.baseImage === 'wolfi') baseImageName = - 'docker.elastic.co/wolfi/chainguard-base:latest@sha256:19764e89441be1f36544f715a738abc1a1898f35ed729486d33172eb54e8d84a'; + 'docker.elastic.co/wolfi/chainguard-base:latest@sha256:082266206be6e559baea1c8b2eeb4fb86ea1318a0cf99cbf0e612dd2c611e80b'; let imageFlavor = ''; if (flags.baseImage === 'ubi') imageFlavor += `-ubi`; - if (flags.baseImage === 'wolfi') imageFlavor += `-wolfi`; + if (flags.baseImage === 'wolfi' && !flags.serverless) imageFlavor += `-wolfi`; if (flags.ironbank) imageFlavor += '-ironbank'; if (flags.cloud) imageFlavor += '-cloud'; if (flags.serverless) imageFlavor += '-serverless'; diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index 0195825313b57..acd5b54a74f1b 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -134,7 +134,7 @@ RUN for iter in {1..10}; do \ (exit $exit_code) {{/ubuntu}} {{#wolfi}} -RUN apk --no-cache add bash curl fontconfig libstdc++ freetype nss findutils shadow +RUN apk --no-cache add bash curl fontconfig libstdc++ nss findutils shadow {{/wolfi}} # Bring in Kibana from the initial stage. diff --git a/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx b/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx index feaecf237e587..ad99c58af1f83 100644 --- a/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx +++ b/src/plugins/ai_assistant_management/selection/public/routes/components/ai_assistant_selection_page.tsx @@ -72,6 +72,7 @@ export function AiAssistantSelectionPage() { <EuiFlexGrid columns={2}> <EuiFlexItem grow> <EuiCard + data-test-subj="aiAssistantSelectionPageObservabilityCard" description={ <div> {!observabilityAIAssistantEnabled ? ( diff --git a/src/plugins/console/public/plugin.ts b/src/plugins/console/public/plugin.ts index d95c24350fb2e..2728629d288b8 100644 --- a/src/plugins/console/public/plugin.ts +++ b/src/plugins/console/public/plugin.ts @@ -157,6 +157,8 @@ export class ConsoleUIPlugin this._embeddableConsole.isEmbeddedConsoleAvailable(); consoleStart.openEmbeddedConsole = (content?: string) => this._embeddableConsole.openEmbeddedConsole(content); + consoleStart.openEmbeddedConsoleAlternateView = () => + this._embeddableConsole.openEmbeddedConsoleAlternateView(); consoleStart.registerEmbeddedConsoleAlternateView = (view: EmbeddedConsoleView | null) => { this._embeddableConsole.registerAlternateView(view); }; diff --git a/src/plugins/console/public/services/embeddable_console.test.ts b/src/plugins/console/public/services/embeddable_console.test.ts index 92cc4d8450906..f35ed8a6fc0bd 100644 --- a/src/plugins/console/public/services/embeddable_console.test.ts +++ b/src/plugins/console/public/services/embeddable_console.test.ts @@ -54,6 +54,49 @@ describe('EmbeddableConsoleInfo', () => { payload: { content: 'GET /_cat/_indices' }, }); }); + it('does nothing if dispatch not set', () => { + eConsole.setDispatch(null); + + eConsole.openEmbeddedConsole(); + + expect(mockDispatch).toHaveBeenCalledTimes(0); + }); + }); + describe('openEmbeddedConsoleAlternateView', () => { + const mockDispatch = jest.fn(); + beforeEach(() => { + jest.clearAllMocks(); + + eConsole.setDispatch(mockDispatch); + }); + it('dispatches open when alt view does not exist', () => { + eConsole.openEmbeddedConsoleAlternateView(); + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith({ + type: 'open', + payload: { alternateView: false }, + }); + }); + it('dispatches open alt view when alt view exists', () => { + eConsole.registerAlternateView({ + ActivationButton: jest.fn(), + ViewContent: jest.fn(), + }); + + eConsole.openEmbeddedConsoleAlternateView(); + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith({ + type: 'open', + payload: { alternateView: true }, + }); + }); + it('does nothing if dispatch not set', () => { + eConsole.setDispatch(null); + + eConsole.openEmbeddedConsoleAlternateView(); + + expect(mockDispatch).toHaveBeenCalledTimes(0); + }); }); describe('getConsoleHeight', () => { it('returns value in storage when found', () => { diff --git a/src/plugins/console/public/services/embeddable_console.ts b/src/plugins/console/public/services/embeddable_console.ts index f5e0197ad833b..2daadfaec5eb4 100644 --- a/src/plugins/console/public/services/embeddable_console.ts +++ b/src/plugins/console/public/services/embeddable_console.ts @@ -47,6 +47,16 @@ export class EmbeddableConsoleInfo { this._dispatch({ type: 'open', payload: content ? { content } : undefined }); } + public openEmbeddedConsoleAlternateView() { + // Embedded Console is not rendered on the page, nothing to do + if (!this._dispatch) return; + + this._dispatch({ + type: 'open', + payload: { alternateView: this._alternateView !== undefined }, + }); + } + public registerAlternateView(view: EmbeddedConsoleView | null) { this._alternateView = view ?? undefined; } diff --git a/src/plugins/console/public/types/plugin_dependencies.ts b/src/plugins/console/public/types/plugin_dependencies.ts index 39b82939619ec..b6c91de724caf 100644 --- a/src/plugins/console/public/types/plugin_dependencies.ts +++ b/src/plugins/console/public/types/plugin_dependencies.ts @@ -59,6 +59,12 @@ export interface ConsolePluginStart { * this function will open the embedded console on the page if it is currently rendered. */ openEmbeddedConsole?: (content?: string) => void; + /** + * openEmbeddedConsoleAlternateView is available if the embedded console can be rendered. + * Calling this function will open the embedded console to the alternative view. If there is no alternative view registered + * this will open the embedded console. + */ + openEmbeddedConsoleAlternateView?: () => void; /** * EmbeddableConsole is a functional component used to render a portable version of the dev tools console on any page in Kibana */ diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.helpers.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.helpers.ts index f1cf9b862ebaa..d71ced1cebd4b 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.helpers.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.helpers.ts @@ -20,15 +20,22 @@ export const defaultProps: Props = { export type FieldEditorTestBed = TestBed & { actions: ReturnType<typeof getCommonActions> }; -export const setup = async (props?: Partial<Props>, deps?: Partial<Context>) => { +export const setup = async ( + props?: Partial<Props>, + deps?: Partial<Context>, + getByNameOverride?: () => any +) => { let testBed: TestBed<string>; await act(async () => { - testBed = await registerTestBed(WithFieldEditorDependencies(FieldEditor, deps), { - memoryRouter: { - wrapComponent: false, - }, - })({ ...defaultProps, ...props }); + testBed = await registerTestBed( + WithFieldEditorDependencies(FieldEditor, deps, getByNameOverride), + { + memoryRouter: { + wrapComponent: false, + }, + } + )({ ...defaultProps, ...props }); }); testBed!.component.update(); diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx index 37b876b327388..8be6c633cda94 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor.test.tsx @@ -121,19 +121,22 @@ describe('<FieldEditor />', () => { }); describe('validation', () => { - test('should accept an optional list of existing fields and prevent creating duplicates', async () => { + test('should prevent creating duplicates', async () => { const existingFields = ['myRuntimeField']; testBed = await setup( { onChange, }, { - namesNotAllowed: { - fields: existingFields, - runtimeComposites: [], - }, - existingConcreteFields: [], fieldTypeToProcess: 'runtime', + }, + // getByName returns a value, which means that the field already exists + () => { + return { + name: 'myRuntimeField', + type: 'boolean', + script: { source: 'emit("hello")' }, + }; } ); @@ -155,7 +158,6 @@ describe('<FieldEditor />', () => { }); test('should not count the default value as a duplicate', async () => { - const existingRuntimeFieldNames = ['myRuntimeField']; const field: Field = { name: 'myRuntimeField', type: 'boolean', @@ -168,11 +170,6 @@ describe('<FieldEditor />', () => { onChange, }, { - namesNotAllowed: { - fields: existingRuntimeFieldNames, - runtimeComposites: [], - }, - existingConcreteFields: [], fieldTypeToProcess: 'runtime', } ); diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts index 7251199af5621..719b717521ca7 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts @@ -18,7 +18,7 @@ import { import { WithFieldEditorDependencies, getCommonActions, - spyIndexPatternGetAllFields, + spyIndexPatternGetByName, spySearchQuery, spySearchQueryResponse, TestDoc, @@ -34,7 +34,7 @@ const defaultProps: Props = { * @param fields The fields of the index pattern */ export const setIndexPatternFields = (fields: Array<{ name: string; displayName: string }>) => { - spyIndexPatternGetAllFields.mockReturnValue(fields); + spyIndexPatternGetByName.mockReturnValue(fields); }; export const getSearchCallMeta = () => { diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts index bbe2a00f31e76..9051a76808cac 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts @@ -21,6 +21,7 @@ import { setSearchResponse, FieldEditorFlyoutContentTestBed, } from './field_editor_flyout_preview.helpers'; +import { spyGetFieldsForWildcard } from './helpers/setup_environment'; import { mockDocuments, createPreviewError } from './helpers/mocks'; describe('Field editor Preview panel', () => { @@ -40,22 +41,23 @@ describe('Field editor Preview panel', () => { const indexPatternFields: Array<{ name: string; displayName: string }> = [ { - name: 'title', - displayName: 'title', + name: 'description', + displayName: 'description', }, { name: 'subTitle', displayName: 'subTitle', }, { - name: 'description', - displayName: 'description', + name: 'title', + displayName: 'title', }, ]; beforeEach(async () => { httpRequestsMockHelpers.setFieldPreviewResponse({ values: ['mockedScriptValue'] }); setIndexPatternFields(indexPatternFields); + spyGetFieldsForWildcard.mockResolvedValue({ fields: indexPatternFields }); setSearchResponse(mockDocuments); setSearchResponseLatency(0); @@ -91,16 +93,16 @@ describe('Field editor Preview panel', () => { expect(getRenderedIndexPatternFields()).toEqual([ { - key: 'title', - value: mockDocuments[0].fields.title, + key: 'description', + value: mockDocuments[0].fields.description, }, { key: 'subTitle', value: mockDocuments[0].fields.subTitle, }, { - key: 'description', - value: mockDocuments[0].fields.description, + key: 'title', + value: mockDocuments[0].fields.title, }, ]); }); @@ -126,8 +128,8 @@ describe('Field editor Preview panel', () => { await setFilterFieldsValue('title'); expect(exists('emptySearchResult')).toBe(false); expect(getRenderedIndexPatternFields()).toEqual([ - { key: 'title', value: 'First doc - title' }, { key: 'subTitle', value: 'First doc - subTitle' }, + { key: 'title', value: 'First doc - title' }, ]); // Should display an empty search result with a button to clear @@ -140,16 +142,16 @@ describe('Field editor Preview panel', () => { component.update(); expect(getRenderedIndexPatternFields()).toEqual([ { - key: 'title', - value: mockDocuments[0].fields.title, + key: 'description', + value: mockDocuments[0].fields.description, }, { key: 'subTitle', value: mockDocuments[0].fields.subTitle, }, { - key: 'description', - value: mockDocuments[0].fields.description, + key: 'title', + value: mockDocuments[0].fields.title, }, ]); }); @@ -174,7 +176,7 @@ describe('Field editor Preview panel', () => { expect(fieldsRendered).not.toBe(null); expect(fieldsRendered!.length).toBe(Object.keys(doc1.fields).length); // make sure that the last one if the "description" field - expect(fieldsRendered!.at(2).text()).toBe('descriptionFirst doc - description'); + expect(fieldsRendered!.at(0).text()).toBe('descriptionFirst doc - description'); // Click the third field in the list ("description") const descriptionField = fieldsRendered!.at(2); @@ -182,8 +184,8 @@ describe('Field editor Preview panel', () => { component.update(); expect(getRenderedIndexPatternFields()).toEqual([ - { key: 'description', value: 'First doc - description' }, // Pinned! { key: 'title', value: 'First doc - title' }, + { key: 'description', value: 'First doc - description' }, // Pinned! { key: 'subTitle', value: 'First doc - subTitle' }, ]); }); @@ -548,39 +550,39 @@ describe('Field editor Preview panel', () => { await fields.updateName('myRuntimeField'); // Give a name to remove empty prompt - expect(getRenderedIndexPatternFields()[0]).toEqual({ + expect(getRenderedIndexPatternFields()[2]).toEqual({ key: 'title', value: doc1.fields.title, }); await goToNextDocument(); - expect(getRenderedIndexPatternFields()[0]).toEqual({ + expect(getRenderedIndexPatternFields()[2]).toEqual({ key: 'title', value: doc2.fields.title, }); await goToNextDocument(); - expect(getRenderedIndexPatternFields()[0]).toEqual({ + expect(getRenderedIndexPatternFields()[2]).toEqual({ key: 'title', value: doc3.fields.title, }); // Going next we circle back to the first document of the list await goToNextDocument(); - expect(getRenderedIndexPatternFields()[0]).toEqual({ + expect(getRenderedIndexPatternFields()[2]).toEqual({ key: 'title', value: doc1.fields.title, }); // Let's go backward await goToPreviousDocument(); - expect(getRenderedIndexPatternFields()[0]).toEqual({ + expect(getRenderedIndexPatternFields()[2]).toEqual({ key: 'title', value: doc3.fields.title, }); await goToPreviousDocument(); - expect(getRenderedIndexPatternFields()[0]).toEqual({ + expect(getRenderedIndexPatternFields()[2]).toEqual({ key: 'title', value: doc2.fields.title, }); @@ -618,7 +620,7 @@ describe('Field editor Preview panel', () => { // First make sure that we have the original cluster data is loaded // and the preview value rendered. - expect(getRenderedIndexPatternFields()[0]).toEqual({ + expect(getRenderedIndexPatternFields()[2]).toEqual({ key: 'title', value: doc1.fields.title, }); @@ -636,16 +638,16 @@ describe('Field editor Preview panel', () => { expect(getRenderedIndexPatternFields()).toEqual([ { - key: 'title', - value: 'loaded doc - title', + key: 'description', + value: 'loaded doc - description', }, { key: 'subTitle', value: 'loaded doc - subTitle', }, { - key: 'description', - value: 'loaded doc - description', + key: 'title', + value: 'loaded doc - title', }, ]); @@ -734,8 +736,8 @@ describe('Field editor Preview panel', () => { expect(exists('documentsNav')).toBe(false); expect(exists('loadDocsFromClusterButton')).toBe(true); expect(getRenderedIndexPatternFields()[0]).toEqual({ - key: 'title', - value: 'loaded doc - title', + key: 'description', + value: 'loaded doc - description', }); }); diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/index.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/index.ts index b29b76a9daf23..6695c6a094834 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/index.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/index.ts @@ -14,7 +14,7 @@ export { WithFieldEditorDependencies, spySearchQuery, spySearchQueryResponse, - spyIndexPatternGetAllFields, + spyIndexPatternGetByName, fieldFormatsOptions, indexPatternNameForTest, setSearchResponseLatency, diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx index 57bbc5edc06ca..2cefb86b1570a 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -18,7 +18,7 @@ import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { fieldFormatsMock as fieldFormats } from '@kbn/field-formats-plugin/common/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { FieldFormat } from '@kbn/field-formats-plugin/common'; -import { createStubDataView } from '@kbn/data-views-plugin/common/data_views/data_view.stub'; +import { createStubDataViewLazy } from '@kbn/data-views-plugin/common/data_views/data_view_lazy.stub'; import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import { PreviewController } from '../../../public/components/preview/preview_controller'; import { FieldEditorProvider, Context } from '../../../public/components/field_editor_context'; @@ -32,7 +32,8 @@ const { search } = dataStart; export const spySearchQuery = jest.fn(); export const spySearchQueryResponse = jest.fn(() => Promise.resolve({})); -export const spyIndexPatternGetAllFields = jest.fn().mockImplementation(() => []); +export const spyIndexPatternGetByName = jest.fn().mockImplementation(() => {}); +export const spyGetFieldsForWildcard = jest.fn().mockResolvedValue({ fields: [] }); let searchResponseDelay = 0; @@ -91,7 +92,8 @@ export const indexPatternNameForTest = 'testIndexPattern'; export const WithFieldEditorDependencies = <T extends object = { [key: string]: unknown }>( Comp: FunctionComponent<T>, - overridingDependencies?: Partial<Context> + overridingDependencies?: Partial<Context>, + getByNameOverride?: () => any ) => (props: T) => { // Setup mocks @@ -119,20 +121,25 @@ export const WithFieldEditorDependencies = return new MockDefaultFieldFormat(); }); - const dataView = createStubDataView({ + const dataView = createStubDataViewLazy({ spec: { title: indexPatternNameForTest, }, + deps: { + apiClient: { + getFieldsForWildcard: spyGetFieldsForWildcard, + }, + }, }); - jest.spyOn(dataView.fields, 'getAll').mockImplementation(spyIndexPatternGetAllFields); + jest + .spyOn(dataView, 'getFieldByName') + .mockImplementation(getByNameOverride || spyIndexPatternGetByName); const dependencies: Context = { dataView, uiSettings: uiSettingsServiceMock.createStartContract(), fieldTypeToProcess: 'runtime', - existingConcreteFields: [], - namesNotAllowed: { fields: [], runtimeComposites: [] }, links: { runtimePainless: 'https://elastic.co', }, @@ -162,6 +169,7 @@ export const WithFieldEditorDependencies = notifications: notificationServiceMock.createStartContract(), }, dataView, + dataViewToUpdate: dataView, onSave: jest.fn(), fieldTypeToProcess: 'runtime', }); diff --git a/src/plugins/data_view_field_editor/public/components/delete_field_provider.tsx b/src/plugins/data_view_field_editor/public/components/delete_field_provider.tsx index fc09b860f705a..d749a1893d647 100644 --- a/src/plugins/data_view_field_editor/public/components/delete_field_provider.tsx +++ b/src/plugins/data_view_field_editor/public/components/delete_field_provider.tsx @@ -27,7 +27,7 @@ export interface Props { } export const getDeleteFieldProvider = ( - modalOpener: (options: OpenFieldDeleteModalOptions) => CloseEditor + modalOpener: (options: OpenFieldDeleteModalOptions) => Promise<CloseEditor> ): React.FunctionComponent<Props> => { return React.memo(({ dataView, children, onDelete }: Props) => { const closeModal = useRef<CloseEditor | null>(null); @@ -36,7 +36,7 @@ export const getDeleteFieldProvider = ( if (closeModal.current) { closeModal.current(); } - closeModal.current = modalOpener({ + closeModal.current = await modalOpener({ ctx: { dataView, }, diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/composite_editor.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/composite_editor.tsx index 53682b17a813e..464446a443d19 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/composite_editor.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/composite_editor.tsx @@ -32,16 +32,12 @@ export interface CompositeEditorProps { } export const CompositeEditor = ({ onReset }: CompositeEditorProps) => { - const { links, existingConcreteFields, subfields$ } = useFieldEditorContext(); + const { links, subfields$ } = useFieldEditorContext(); const subfields = useObservable(subfields$) || {}; return ( <div data-test-subj="compositeEditor"> - <ScriptField - existingConcreteFields={existingConcreteFields} - links={links} - placeholder={"emit('field_name', 'hello world');"} - /> + <ScriptField links={links} placeholder={"emit('field_name', 'hello world');"} /> <EuiSpacer size="xl" /> <> <EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="spaceBetween"> diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/field_detail.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/field_detail.tsx index 4914ff82c5bfb..eeb49ccbb72f4 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/field_detail.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/field_detail.tsx @@ -79,7 +79,7 @@ const geti18nTexts = (): { }); export const FieldDetail = ({}) => { - const { links, existingConcreteFields, fieldTypeToProcess } = useFieldEditorContext(); + const { links, fieldTypeToProcess } = useFieldEditorContext(); const i18nTexts = geti18nTexts(); return ( <> @@ -114,7 +114,7 @@ export const FieldDetail = ({}) => { data-test-subj="valueRow" withDividerRule > - <ScriptField existingConcreteFields={existingConcreteFields} links={links} /> + <ScriptField links={links} /> </FormRow> )} diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/field_editor.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/field_editor.tsx index 790b44f03701e..b7725f6d105bc 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/field_editor.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/field_editor.tsx @@ -107,7 +107,7 @@ const formSerializer = (field: FieldFormInternal): Field => { }; const FieldEditorComponent = ({ field, onChange, onFormModifiedChange }: Props) => { - const { namesNotAllowed, fieldTypeToProcess, fieldName$, subfields$ } = useFieldEditorContext(); + const { fieldTypeToProcess, fieldName$, subfields$, dataView } = useFieldEditorContext(); const { params: { update: updatePreviewParams }, fieldPreview$, @@ -121,7 +121,7 @@ const FieldEditorComponent = ({ field, onChange, onFormModifiedChange }: Props) const { submit, isValid: isFormValid, isSubmitted, getFields, isSubmitting } = form; - const nameFieldConfig = getNameFieldConfig(namesNotAllowed, field); + const nameFieldConfig = getNameFieldConfig(dataView, field); const [formData] = useFormData<FieldFormInternal>({ form }); const isFormModified = useFormIsModified({ diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/format_field.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/format_field.tsx index 9518ba6cc89ed..a9d63ca5d5b88 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/format_field.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/format_field.tsx @@ -20,7 +20,7 @@ import { FormatSelectEditor } from '../../field_format_editor'; import type { FieldFormInternal } from '../field_editor'; export const FormatField = () => { - const { dataView, uiSettings, fieldFormats, fieldFormatEditors } = useFieldEditorContext(); + const { uiSettings, fieldFormats, fieldFormatEditors } = useFieldEditorContext(); const isMounted = useRef(false); const [{ type }] = useFormData<FieldFormInternal>({ watch: ['name', 'type'] }); const { getFields, isSubmitted } = useFormContext(); @@ -67,7 +67,6 @@ export const FormatField = () => { <FormatSelectEditor esTypes={typeValue || (['keyword'] as ES_FIELD_TYPES[])} - indexPattern={dataView} fieldFormatEditors={fieldFormatEditors} fieldFormats={fieldFormats} uiSettings={uiSettings} diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx index 7d57a1828f822..d353c8e31988f 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx @@ -32,7 +32,6 @@ import { PreviewState } from '../../preview/types'; interface Props { links: { runtimePainless: string }; - existingConcreteFields?: Array<{ name: string; type: string }>; placeholder?: string; } @@ -60,8 +59,9 @@ const currentDocumentIsLoadingSelector = (state: PreviewState) => state.isLoadin const currentErrorSelector = (state: PreviewState) => state.previewResponse?.error; const isLoadingPreviewSelector = (state: PreviewState) => state.isLoadingPreview; const isPreviewAvailableSelector = (state: PreviewState) => state.isPreviewAvailable; +const concreteFieldsSelector = (state: PreviewState) => state.concreteFields; -const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Props) => { +const ScriptFieldComponent = ({ links, placeholder }: Props) => { const { validation: { setScriptEditorValidation }, } = useFieldPreviewContext(); @@ -75,6 +75,13 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr const isFetchingDoc = useStateSelector(controller.state$, currentDocumentIsLoadingSelector); const isLoadingPreview = useStateSelector(controller.state$, isLoadingPreviewSelector); const isPreviewAvailable = useStateSelector(controller.state$, isPreviewAvailableSelector); + /** + * An array of existing concrete fields. If the user gives a name to the runtime + * field that matches one of the concrete fields, a callout will be displayed + * to indicate that this runtime field will shadow the concrete field. + * It is also used to provide the list of field autocomplete suggestions to the code editor. + */ + const concreteFields = useStateSelector(controller.state$, concreteFieldsSelector); const [validationData$, nextValidationData$] = useBehaviorSubject< | { isFetchingDoc: boolean; @@ -91,8 +98,8 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr const currentDocId = currentDocument?._id; const suggestionProvider = useMemo( - () => PainlessLang.getSuggestionProvider(painlessContext, existingConcreteFields), - [painlessContext, existingConcreteFields] + () => PainlessLang.getSuggestionProvider(painlessContext, concreteFields), + [painlessContext, concreteFields] ); const { validateFields } = useFormContext(); diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/lib.ts b/src/plugins/data_view_field_editor/public/components/field_editor/lib.ts index 4defeab116047..95971161b549b 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/lib.ts +++ b/src/plugins/data_view_field_editor/public/components/field_editor/lib.ts @@ -28,9 +28,13 @@ export interface Change { export type ChangeSet = Record<string, Change>; const createNameNotAllowedValidator = - (namesNotAllowed: Context['namesNotAllowed']): ValidationFunc<{}, string, string> => - ({ value }) => { - if (namesNotAllowed.fields.includes(value)) { + (dataView: Context['dataView'], fieldName?: string): ValidationFunc<{}, string, string> => + async ({ value }) => { + const runtimeComposites = Object.entries(dataView.getAllRuntimeFields()) + .filter(([, _runtimeField]) => _runtimeField.type === 'composite') + .map(([_runtimeFieldName]) => _runtimeFieldName); + + if (value !== fieldName && (await dataView.getFieldByName(value, true))) { return { message: i18n.translate( 'indexPatternFieldEditor.editor.runtimeFieldsEditor.existRuntimeFieldNamesValidationErrorMessage', @@ -39,7 +43,7 @@ const createNameNotAllowedValidator = } ), }; - } else if (namesNotAllowed.runtimeComposites.includes(value)) { + } else if (value !== fieldName && runtimeComposites.includes(value)) { return { message: i18n.translate( 'indexPatternFieldEditor.editor.runtimeFieldsEditor.existCompositeNamesValidationErrorMessage', @@ -55,31 +59,21 @@ const createNameNotAllowedValidator = * Dynamically retrieve the config for the "name" field, adding * a validator to avoid duplicated runtime fields to be created. * - * @param namesNotAllowed Array of names not allowed for the field "name" * @param field Initial value of the form */ export const getNameFieldConfig = ( - namesNotAllowed?: Context['namesNotAllowed'], + dataView: Context['dataView'], field?: Props['field'] ): FieldConfig<string, Field> => { const nameFieldConfig = schema.name as FieldConfig<string, Field>; - if (!namesNotAllowed) { - return nameFieldConfig; - } - - const filterOutCurrentFieldName = (name: string) => name !== field?.name; - // Add validation to not allow duplicates return { ...nameFieldConfig!, validations: [ ...(nameFieldConfig.validations ?? []), { - validator: createNameNotAllowedValidator({ - fields: namesNotAllowed.fields.filter(filterOutCurrentFieldName), - runtimeComposites: namesNotAllowed.runtimeComposites.filter(filterOutCurrentFieldName), - }), + validator: createNameNotAllowedValidator(dataView, field?.name), }, ], }; diff --git a/src/plugins/data_view_field_editor/public/components/field_editor_context.tsx b/src/plugins/data_view_field_editor/public/components/field_editor_context.tsx index 98d8b7ecd215a..52bb6bf0ab1e2 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor_context.tsx @@ -16,7 +16,7 @@ import React, { import { NotificationsStart, CoreStart } from '@kbn/core/public'; import type { BehaviorSubject } from 'rxjs'; import type { - DataView, + DataViewLazy, DataPublicPluginStart, FieldFormatsStart, RuntimeFieldSubFields, @@ -25,7 +25,7 @@ import { ApiService } from '../lib/api'; import type { InternalFieldType, PluginStart } from '../types'; export interface Context { - dataView: DataView; + dataView: DataViewLazy; fieldTypeToProcess: InternalFieldType; uiSettings: CoreStart['uiSettings']; links: { @@ -38,22 +38,7 @@ export interface Context { }; fieldFormatEditors: PluginStart['fieldFormatEditors']; fieldFormats: FieldFormatsStart; - /** - * An array of field names not allowed. - * e.g we probably don't want a user to give a name of an existing - * runtime field (for that the user should edit the existing runtime field). - */ - namesNotAllowed: { - fields: string[]; - runtimeComposites: string[]; - }; - /** - * An array of existing concrete fields. If the user gives a name to the runtime - * field that matches one of the concrete fields, a callout will be displayed - * to indicate that this runtime field will shadow the concrete field. - * It is also used to provide the list of field autocomplete suggestions to the code editor. - */ - existingConcreteFields: Array<{ name: string; type: string }>; + fieldName$: BehaviorSubject<string>; subfields$: BehaviorSubject<RuntimeFieldSubFields | undefined>; } @@ -68,8 +53,6 @@ export const FieldEditorProvider: FunctionComponent<PropsWithChildren<Context>> fieldTypeToProcess, fieldFormats, fieldFormatEditors, - namesNotAllowed, - existingConcreteFields, children, fieldName$, subfields$, @@ -83,8 +66,6 @@ export const FieldEditorProvider: FunctionComponent<PropsWithChildren<Context>> services, fieldFormats, fieldFormatEditors, - namesNotAllowed, - existingConcreteFields, fieldName$, subfields$, }), @@ -96,8 +77,6 @@ export const FieldEditorProvider: FunctionComponent<PropsWithChildren<Context>> uiSettings, fieldFormats, fieldFormatEditors, - namesNotAllowed, - existingConcreteFields, fieldName$, subfields$, ] diff --git a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx index b8a1911025292..7f484c35b20f4 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -11,8 +11,9 @@ import { DocLinksStart, NotificationsStart, CoreStart } from '@kbn/core/public'; import { BehaviorSubject } from 'rxjs'; import { - DataViewField, DataView, + DataViewField, + DataViewLazy, DataPublicPluginStart, UsageCollectionStart, DataViewsPublicPluginStart, @@ -37,7 +38,8 @@ export interface Props { /** The docLinks start service from core */ docLinks: DocLinksStart; /** The index pattern where the field will be added */ - dataView: DataView; + dataView: DataViewLazy; + dataViewToUpdate: DataView | DataViewLazy; /** The Kibana field type of the field to create or edit (default: "runtime") */ fieldTypeToProcess: InternalFieldType; /** Optional field to edit */ @@ -72,6 +74,7 @@ export const FieldEditorFlyoutContentContainer = ({ docLinks, fieldTypeToProcess, dataView, + dataViewToUpdate, dataViews, search, notifications, @@ -92,6 +95,7 @@ export const FieldEditorFlyoutContentContainer = ({ notifications, }, dataView, + dataViewToUpdate, onSave, fieldToEdit, fieldTypeToProcess, @@ -116,8 +120,6 @@ export const FieldEditorFlyoutContentContainer = ({ services={services} fieldFormatEditors={fieldFormatEditors} fieldFormats={fieldFormats} - namesNotAllowed={controller.getNamesNotAllowed()} - existingConcreteFields={controller.getExistingConcreteFields()} fieldName$={new BehaviorSubject(fieldToEdit?.name || '')} subfields$={new BehaviorSubject(fieldToEdit?.fields)} > diff --git a/src/plugins/data_view_field_editor/public/components/field_format_editor/field_format_editor.tsx b/src/plugins/data_view_field_editor/public/components/field_format_editor/field_format_editor.tsx index de90fcb73abe7..34c7b54ec4c4e 100644 --- a/src/plugins/data_view_field_editor/public/components/field_format_editor/field_format_editor.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_format_editor/field_format_editor.tsx @@ -9,7 +9,6 @@ import { EuiCode, EuiFormRow, EuiSelect } from '@elastic/eui'; import { CoreStart } from '@kbn/core/public'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/data-plugin/public'; -import { DataView } from '@kbn/data-views-plugin/public'; import type { FieldFormatInstanceType, FieldFormatParams, @@ -25,7 +24,6 @@ import { FormatEditor } from './format_editor'; export interface FormatSelectEditorProps { esTypes: ES_FIELD_TYPES[]; - indexPattern: DataView; fieldFormatEditors: FormatEditorServiceStart['fieldFormatEditors']; fieldFormats: FieldFormatsStart; uiSettings: CoreStart['uiSettings']; diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_list/field_list.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_list/field_list.tsx index 598b6c5b1ab34..6011681586333 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_list/field_list.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_list/field_list.tsx @@ -50,6 +50,7 @@ function fuzzyMatch(searchValue: string, text: string) { const pinnedFieldsSelector = (s: PreviewState) => s.pinnedFields; const currentDocumentSelector = (s: PreviewState) => s.documents[s.currentIdx]; +const fieldMapSelector = (s: PreviewState) => s.fieldMap; interface RowProps { index: number; @@ -74,13 +75,13 @@ export const PreviewFieldList: React.FC<Props> = ({ height, clearSearch, searchV const { controller } = useFieldPreviewContext(); const pinnedFields = useStateSelector(controller.state$, pinnedFieldsSelector, isEqual); const currentDocument = useStateSelector(controller.state$, currentDocumentSelector); + const fieldMap = useStateSelector(controller.state$, fieldMapSelector); const [showAllFields, setShowAllFields] = useState(false); const fieldList: DocumentField[] = useMemo( () => - dataView.fields - .getAll() + Object.values(fieldMap) .map((field) => { const { name, displayName } = field; const formatter = dataView.getFormatterForField(field); @@ -95,7 +96,7 @@ export const PreviewFieldList: React.FC<Props> = ({ height, clearSearch, searchV }; }) .filter(({ value }) => value !== undefined), - [dataView, currentDocument?.fields] + [dataView, fieldMap, currentDocument?.fields] ); const fieldListWithPinnedFields: DocumentField[] = useMemo(() => { diff --git a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx index ee510a3a0791f..f8226374d4086 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { DataView, + DataViewLazy, DataViewField, DataViewsPublicPluginStart, } from '@kbn/data-views-plugin/public'; @@ -36,7 +37,8 @@ export const defaultValueFormatter = (value: unknown) => { }; interface PreviewControllerArgs { - dataView: DataView; + dataView: DataViewLazy; + dataViewToUpdate: DataView | DataViewLazy; onSave: (field: DataViewField[]) => void; fieldToEdit?: Field; fieldTypeToProcess: InternalFieldType; @@ -79,13 +81,25 @@ const previewStateDefault: PreviewState = { /** Flag to show/hide the preview panel */ isPanelVisible: true, isSaving: false, + concreteFields: [], + fieldMap: {}, }; export class PreviewController { - constructor({ deps, dataView, onSave, fieldToEdit, fieldTypeToProcess }: PreviewControllerArgs) { + constructor({ + deps, + // using two different data view references while API consumers might be passing in + // dataView or dataViewLazy. Don't want to rely on DataView with full field list. + dataView, + dataViewToUpdate, + onSave, + fieldToEdit, + fieldTypeToProcess, + }: PreviewControllerArgs) { this.deps = deps; this.dataView = dataView; + this.dataViewToUpdate = dataViewToUpdate; this.onSave = onSave; this.fieldToEdit = fieldToEdit; @@ -98,10 +112,14 @@ export class PreviewController { this.state$ = this.internalState$ as BehaviorObservable<PreviewState>; this.fetchSampleDocuments(); + + this.setExistingConcreteFields(); + this.setFieldList(); } // dependencies - private dataView: DataView; + private dataView: DataViewLazy; + private dataViewToUpdate: DataView | DataViewLazy; private deps: { search: ISearchStart; @@ -120,11 +138,6 @@ export class PreviewController { private previewCount = 0; - private namesNotAllowed?: { - fields: string[]; - runtimeComposites: string[]; - }; - private updateState = (newState: Partial<PreviewState>) => { this.internalState$.next({ ...this.state$.getValue(), ...newState }); }; @@ -139,68 +152,81 @@ export class PreviewController { documentId: undefined, }; - getNamesNotAllowed = () => { - if (!this.namesNotAllowed) { - const fieldNames = this.dataView.fields.map((fld) => fld.name); - const runtimeCompositeNames = Object.entries(this.dataView.getAllRuntimeFields()) - .filter(([, _runtimeField]) => _runtimeField.type === 'composite') - .map(([_runtimeFieldName]) => _runtimeFieldName); - this.namesNotAllowed = { - fields: fieldNames, - runtimeComposites: runtimeCompositeNames, - }; - } + private setFieldList = async () => { + const fieldMap = ( + await this.dataView.getFields({ + fieldName: ['*'], + scripted: false, + runtime: false, + }) + ).getFieldMapSorted(); - return this.namesNotAllowed; + this.updateState({ fieldMap }); }; - getExistingConcreteFields = () => { + private setExistingConcreteFields = async () => { const existing: Array<{ name: string; type: string }> = []; - this.dataView.fields - .filter((fld) => { - const isFieldBeingEdited = this.fieldToEdit?.name === fld.name; - return !isFieldBeingEdited && fld.isMapped; + const fieldMap = ( + await this.dataView.getFields({ + fieldName: ['*'], + scripted: false, + runtime: false, }) - .forEach((fld) => { - existing.push({ - name: fld.name, - type: (fld.esTypes && fld.esTypes[0]) || '', - }); + ).getFieldMap(); + + // remove name of currently edited field + if (this.fieldToEdit?.name) { + delete fieldMap[this.fieldToEdit?.name]; + } + + Object.values(fieldMap).forEach((fld) => { + existing.push({ + name: fld.name, + type: (fld.esTypes && fld.esTypes[0]) || '', }); + }); - return existing; + this.updateState({ concreteFields: existing }); }; - updateConcreteField = (updatedField: Field): DataViewField[] => { - const editedField = this.dataView.getFieldByName(updatedField.name); + updateConcreteField = async (updatedField: Field): Promise<DataViewField[]> => { + const editedField = await this.dataViewToUpdate.getFieldByName(updatedField.name); if (!editedField) { throw new Error( `Unable to find field named '${ updatedField.name - }' on index pattern '${this.dataView.getIndexPattern()}'` + }' on index pattern '${this.dataViewToUpdate.getIndexPattern()}'` ); } // Update custom label, popularity and format + this.dataViewToUpdate.setFieldCustomLabel(updatedField.name, updatedField.customLabel); this.dataView.setFieldCustomLabel(updatedField.name, updatedField.customLabel); + this.dataViewToUpdate.setFieldCustomDescription( + updatedField.name, + updatedField.customDescription + ); this.dataView.setFieldCustomDescription(updatedField.name, updatedField.customDescription); if (updatedField.popularity !== undefined) { + this.dataViewToUpdate.setFieldCount(updatedField.name, updatedField.popularity || 0); this.dataView.setFieldCount(updatedField.name, updatedField.popularity || 0); } if (updatedField.format) { + this.dataViewToUpdate.setFieldFormat(updatedField.name, updatedField.format!); this.dataView.setFieldFormat(updatedField.name, updatedField.format!); } else { + this.dataViewToUpdate.deleteFieldFormat(updatedField.name); this.dataView.deleteFieldFormat(updatedField.name); } return [editedField]; }; - updateRuntimeField = (updatedField: Field): DataViewField[] => { + updateRuntimeField = async (updatedField: Field): Promise<DataViewField[]> => { const nameHasChanged = Boolean(this.fieldToEdit) && this.fieldToEdit!.name !== updatedField.name; const typeHasChanged = @@ -211,6 +237,7 @@ export class PreviewController { const { script } = updatedField; + // this seems a bit convoluted if (this.fieldTypeToProcess === 'runtime') { try { this.deps.usageCollection.reportUiCounter(pluginName, METRIC_TYPE.COUNT, 'save_runtime'); @@ -218,9 +245,15 @@ export class PreviewController { } catch {} // rename an existing runtime field if (nameHasChanged || hasChangeToOrFromComposite) { + this.dataViewToUpdate.removeRuntimeField(this.fieldToEdit!.name); this.dataView.removeRuntimeField(this.fieldToEdit!.name); } + this.dataViewToUpdate.addRuntimeField(updatedField.name, { + type: updatedField.type as RuntimeType, + script, + fields: updatedField.fields, + }); this.dataView.addRuntimeField(updatedField.name, { type: updatedField.type as RuntimeType, script, @@ -233,7 +266,8 @@ export class PreviewController { } catch {} } - return this.dataView.addRuntimeField(updatedField.name, updatedField); + this.dataView.addRuntimeField(updatedField.name, updatedField); + return this.dataViewToUpdate.addRuntimeField(updatedField.name, updatedField); }; saveField = async (updatedField: Field) => { @@ -251,8 +285,8 @@ export class PreviewController { try { const editedFields: DataViewField[] = this.fieldTypeToProcess === 'runtime' - ? this.updateRuntimeField(updatedField) - : this.updateConcreteField(updatedField as Field); + ? await this.updateRuntimeField(updatedField) + : await this.updateConcreteField(updatedField as Field); const afterSave = () => { const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', { @@ -264,8 +298,8 @@ export class PreviewController { this.onSave(editedFields); }; - if (this.dataView.isPersisted()) { - await this.deps.dataViews.updateSavedObject(this.dataView); + if (this.dataViewToUpdate.isPersisted()) { + await this.deps.dataViews.updateSavedObject(this.dataViewToUpdate); } afterSave(); diff --git a/src/plugins/data_view_field_editor/public/components/preview/types.ts b/src/plugins/data_view_field_editor/public/components/preview/types.ts index 143f25f0c90ac..63f7da71abfb1 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/types.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/types.ts @@ -12,6 +12,7 @@ import type { RuntimeField, SerializedFieldFormat, RuntimePrimitiveTypes, + DataViewField, } from '../../shared_imports'; import type { RuntimeFieldPainlessError } from '../../types'; import type { PreviewController } from './preview_controller'; @@ -69,6 +70,8 @@ export interface PreviewState { isPreviewAvailable: boolean; isPanelVisible: boolean; isSaving: boolean; + concreteFields: Array<{ name: string; type: string }>; + fieldMap: Record<string, DataViewField>; } export interface FetchDocError { diff --git a/src/plugins/data_view_field_editor/public/lib/remove_fields.ts b/src/plugins/data_view_field_editor/public/lib/remove_fields.ts index e3649569d5eb9..f2245c8eec34a 100644 --- a/src/plugins/data_view_field_editor/public/lib/remove_fields.ts +++ b/src/plugins/data_view_field_editor/public/lib/remove_fields.ts @@ -10,18 +10,28 @@ import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; import { NotificationsStart } from '@kbn/core/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { DataView, UsageCollectionStart } from '../shared_imports'; +import { DataView, DataViewLazy, UsageCollectionStart } from '../shared_imports'; import { pluginName } from '../constants'; export async function removeFields( fieldNames: string[], - dataView: DataView, + dataView: DataView | DataViewLazy, services: { dataViews: DataViewsPublicPluginStart; usageCollection: UsageCollectionStart; notifications: NotificationsStart; } ) { + // if we're not handed a DataViewLazy, then check to see if there's one in use + // it'll be in the cache + if (dataView.id && !(dataView instanceof DataViewLazy)) { + const lazy = await services.dataViews.getDataViewLazyFromCache(dataView.id); + if (lazy) { + fieldNames.forEach((fieldName) => { + lazy.removeRuntimeField(fieldName); + }); + } + } fieldNames.forEach((fieldName) => { dataView.removeRuntimeField(fieldName); }); diff --git a/src/plugins/data_view_field_editor/public/open_delete_modal.tsx b/src/plugins/data_view_field_editor/public/open_delete_modal.tsx index 0277563631250..139569275d16c 100644 --- a/src/plugins/data_view_field_editor/public/open_delete_modal.tsx +++ b/src/plugins/data_view_field_editor/public/open_delete_modal.tsx @@ -13,6 +13,7 @@ import { toMountPoint, DataViewsPublicPluginStart, DataView, + DataViewLazy, UsageCollectionStart, } from './shared_imports'; @@ -29,7 +30,7 @@ export interface OpenFieldDeleteModalOptions { * Config for the delete modal */ ctx: { - dataView: DataView; + dataView: DataView | DataViewLazy; }; /** * Callback fired when fields are deleted @@ -60,9 +61,9 @@ export class DeleteCompositeSubfield extends Error { export const getFieldDeleteModalOpener = ({ core, dataViews, usageCollection }: Dependencies) => - (options: OpenFieldDeleteModalOptions): CloseEditor => { + async (options: OpenFieldDeleteModalOptions): Promise<CloseEditor> => { if (typeof options.fieldName === 'string') { - const fieldToDelete = options.ctx.dataView.getFieldByName(options.fieldName); + const fieldToDelete = await options.ctx.dataView.getFieldByName(options.fieldName); // we can check for composite type since composite runtime field definitions themselves don't become fields const doesBelongToCompositeField = fieldToDelete?.runtimeField?.type === 'composite'; diff --git a/src/plugins/data_view_field_editor/public/open_editor.tsx b/src/plugins/data_view_field_editor/public/open_editor.tsx index e6caeacdfa7d9..3cdbbd7dc6224 100644 --- a/src/plugins/data_view_field_editor/public/open_editor.tsx +++ b/src/plugins/data_view_field_editor/public/open_editor.tsx @@ -20,9 +20,8 @@ import type { DataViewsPublicPluginStart, FieldFormatsStart, DataViewField, - DataViewLazy, } from './shared_imports'; -import { DataView } from './shared_imports'; +import { DataView, DataViewLazy } from './shared_imports'; import { createKibanaReactContext } from './shared_imports'; import type { CloseEditor, Field, InternalFieldType, PluginStart } from './types'; @@ -130,13 +129,14 @@ export const getFieldEditorOpener = }; }; - const dataView = - dataViewLazyOrNot instanceof DataView + const dataViewLazy = + dataViewLazyOrNot instanceof DataViewLazy ? dataViewLazyOrNot - : await dataViews.toDataView(dataViewLazyOrNot); + : await dataViews.toDataViewLazy(dataViewLazyOrNot); const dataViewField = fieldNameToEdit - ? dataView.getFieldByName(fieldNameToEdit) || getRuntimeField(fieldNameToEdit) + ? (await dataViewLazy.getFieldByName(fieldNameToEdit, true)) || + getRuntimeField(fieldNameToEdit) : undefined; if (fieldNameToEdit && !dataViewField) { @@ -168,8 +168,8 @@ export const getFieldEditorOpener = customLabel: dataViewField.customLabel, customDescription: dataViewField.customDescription, popularity: dataViewField.count, - format: dataView.getFormatterForFieldNoDefault(fieldNameToEdit!)?.toJSON(), - ...dataView.getRuntimeField(fieldNameToEdit!)!, + format: dataViewLazy.getFormatterForFieldNoDefault(fieldNameToEdit!)?.toJSON(), + ...dataViewLazy.getRuntimeField(fieldNameToEdit!)!, }; } else { // Concrete field @@ -179,7 +179,7 @@ export const getFieldEditorOpener = customLabel: dataViewField.customLabel, customDescription: dataViewField.customDescription, popularity: dataViewField.count, - format: dataView.getFormatterForFieldNoDefault(fieldNameToEdit!)?.toJSON(), + format: dataViewLazy.getFormatterForFieldNoDefault(fieldNameToEdit!)?.toJSON(), parentName: dataViewField.spec.parentName, }; } @@ -195,7 +195,11 @@ export const getFieldEditorOpener = fieldToEdit={field} fieldToCreate={fieldToCreate} fieldTypeToProcess={fieldTypeToProcess} - dataView={dataView} + // currently using two dataView versions since API consumer is still potentially using legacy dataView + // this is what is used internally + dataView={dataViewLazy} + // this is what has been passed by API consumer + dataViewToUpdate={dataViewLazyOrNot} search={search} dataViews={dataViews} notifications={notifications} diff --git a/src/plugins/data_view_field_editor/public/plugin.test.tsx b/src/plugins/data_view_field_editor/public/plugin.test.tsx index 317faa7bf5c63..4f5c773dd7a4e 100644 --- a/src/plugins/data_view_field_editor/public/plugin.test.tsx +++ b/src/plugins/data_view_field_editor/public/plugin.test.tsx @@ -118,7 +118,7 @@ describe('DataViewFieldEditorPlugin', () => { isPersisted: () => true, } as unknown as DataView; - openDeleteModal({ + await openDeleteModal({ onDelete: onDeleteSpy, ctx: { dataView: indexPatternMock }, fieldName: ['a', 'b', 'c'], @@ -147,7 +147,7 @@ describe('DataViewFieldEditorPlugin', () => { test('should return a handler to close the modal', async () => { const { openDeleteModal } = plugin.start(coreStart, pluginStart); - const closeModal = openDeleteModal({ fieldName: ['a'], ctx: { dataView: {} as any } }); + const closeModal = await openDeleteModal({ fieldName: ['a'], ctx: { dataView: {} as any } }); expect(typeof closeModal).toBe('function'); }); diff --git a/src/plugins/data_view_field_editor/public/shared_imports.ts b/src/plugins/data_view_field_editor/public/shared_imports.ts index 8042b1594c618..295e15d383c83 100644 --- a/src/plugins/data_view_field_editor/public/shared_imports.ts +++ b/src/plugins/data_view_field_editor/public/shared_imports.ts @@ -9,7 +9,7 @@ export type { DataPublicPluginStart } from '@kbn/data-plugin/public'; export type { DataViewsPublicPluginStart, DataViewField } from '@kbn/data-views-plugin/public'; -export { DataView } from '@kbn/data-views-plugin/public'; +export { DataView, DataViewLazy } from '@kbn/data-views-plugin/public'; export type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; export type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; @@ -21,7 +21,6 @@ export type { RuntimeFieldSubField, RuntimeFieldSubFields, RuntimePrimitiveTypes, - DataViewLazy, } from '@kbn/data-views-plugin/common'; export { KBN_FIELD_TYPES, ES_FIELD_TYPES } from '@kbn/data-plugin/common'; diff --git a/src/plugins/data_view_field_editor/public/types.ts b/src/plugins/data_view_field_editor/public/types.ts index 46c0725d78339..9d872694a21e8 100644 --- a/src/plugins/data_view_field_editor/public/types.ts +++ b/src/plugins/data_view_field_editor/public/types.ts @@ -43,7 +43,7 @@ export interface PluginStart { * Method to open the data view field delete fly-out * @param options Configuration options for the fly-out */ - openDeleteModal(options: OpenFieldDeleteModalOptions): CloseEditor; + openDeleteModal(options: OpenFieldDeleteModalOptions): Promise<CloseEditor>; fieldFormatEditors: FormatEditorServiceStart['fieldFormatEditors']; /** * Convenience method for user permissions checks diff --git a/src/plugins/data_views/common/data_views/data_view_lazy.stub.ts b/src/plugins/data_views/common/data_views/data_view_lazy.stub.ts new file mode 100644 index 0000000000000..a6be3083e63fd --- /dev/null +++ b/src/plugins/data_views/common/data_views/data_view_lazy.stub.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FieldFormatsStartCommon } from '@kbn/field-formats-plugin/common'; +import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; +import { DataViewLazy } from './data_view_lazy'; +import { DataViewSpec } from '../types'; + +/** + * Create a custom stub index pattern. Use it in your unit tests where an {@link DataViewLazy} expected. + * @param spec - Serialized index pattern object + * @param opts - Specify index pattern options + * @param deps - Optionally provide dependencies, you can provide a custom field formats implementation, by default a dummy mock is used + * + * @returns - an {@link DataViewLazy} instance + * + * + * @example + * + * You can provide a custom implementation or assert calls using jest.spyOn: + * + * ```ts + * const indexPattern = createStubIndexPattern({spec: {title: 'logs-*'}}); + * const spy = jest.spyOn(indexPattern, 'getFormatterForField'); + * + * // use `spy` as a regular jest mock + * + * ``` + */ +export const createStubDataViewLazy = ({ + spec, + opts, + deps, +}: { + spec: DataViewSpec; + opts?: { + shortDotsEnable?: boolean; + metaFields?: string[]; + }; + deps?: { + fieldFormats?: FieldFormatsStartCommon; + apiClient?: any; + }; +}): DataViewLazy => { + return new DataViewLazy({ + spec: { version: '1', ...spec }, + metaFields: opts?.metaFields ?? ['_id', '_type', '_source'], + shortDotsEnable: opts?.shortDotsEnable, + fieldFormats: deps?.fieldFormats ?? fieldFormatsMock, + apiClient: { + getFieldsForWildcard: jest.fn().mockResolvedValue({ fields: [] }), + ...deps?.apiClient, + }, + scriptedFieldsEnabled: true, + }); +}; diff --git a/src/plugins/data_views/common/data_views/data_view_lazy.test.ts b/src/plugins/data_views/common/data_views/data_view_lazy.test.ts index 836ef451dafbe..1ff5ae29f490e 100644 --- a/src/plugins/data_views/common/data_views/data_view_lazy.test.ts +++ b/src/plugins/data_views/common/data_views/data_view_lazy.test.ts @@ -491,6 +491,9 @@ describe('DataViewLazy', () => { Object.values((await dataViewLazy.getFields({ fieldName: ['*'] })).getFieldMap()).length - fieldCount ).toEqual(2); + expect(Object.keys(dataViewLazy.getRuntimeFields({ fieldName: ['new_field.a'] }))).toEqual([ + 'new_field.a', + ]); expect(dataViewLazy.getRuntimeField('new_field')).toMatchSnapshot(); expect((await dataViewLazy.toSpec(toSpecGetAllFields))!.fields!['new_field.a']).toBeDefined(); expect((await dataViewLazy.toSpec(toSpecGetAllFields))!.fields!['new_field.b']).toBeDefined(); diff --git a/src/plugins/data_views/common/data_views/data_view_lazy.ts b/src/plugins/data_views/common/data_views/data_view_lazy.ts index f991fbf4b44d8..95bbc21937cf1 100644 --- a/src/plugins/data_views/common/data_views/data_view_lazy.ts +++ b/src/plugins/data_views/common/data_views/data_view_lazy.ts @@ -120,7 +120,7 @@ export class DataViewLazy extends AbstractDataView { public getRuntimeFields = ({ fieldName = ['*'] }: Pick<GetFieldsParams, 'fieldName'>) => // getRuntimeFieldSpecMap flattens composites into a list of fields - Object.values(this.getRuntimeFieldSpecMap({ fieldName })).reduce<DataViewFieldMap>( + Object.values(this.getRuntimeFieldSpecMap({ fieldName: ['*'] })).reduce<DataViewFieldMap>( (col, field) => { if (!fieldMatchesFieldsRequested(field.name, fieldName)) { return col; diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index 099059a1ec335..4647bc47236ac 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -294,7 +294,7 @@ export interface DataViewsServicePublicMethods { * @param displayErrors - If set false, API consumer is responsible for displaying and handling errors. */ updateSavedObject: ( - indexPattern: DataView, + indexPattern: AbstractDataView, saveAttempts?: number, ignoreErrors?: boolean, displayErrors?: boolean @@ -315,6 +315,7 @@ export interface DataViewsServicePublicMethods { getAllDataViewLazy: () => Promise<DataViewLazy[]>; getDataViewLazy: (id: string) => Promise<DataViewLazy>; + getDataViewLazyFromCache: (id: string) => Promise<DataViewLazy | undefined>; createDataViewLazy: (spec: DataViewSpec) => Promise<DataViewLazy>; @@ -512,8 +513,10 @@ export class DataViewsService { */ clearInstanceCache = (id?: string) => { if (id) { + this.dataViewLazyCache.delete(id); this.dataViewCache.delete(id); } else { + this.dataViewLazyCache.clear(); this.dataViewCache.clear(); } }; @@ -1011,6 +1014,10 @@ export class DataViewsService { } }; + getDataViewLazyFromCache = async (id: string) => { + return this.dataViewLazyCache.get(id); + }; + /** * Get an index pattern by id, cache optimized. * @param id @@ -1436,7 +1443,7 @@ export class DataViewsService { // unsaved DataViewLazy changes will not be reflected in the returned DataView async toDataView(dataViewLazy: DataViewLazy) { // if persisted - if (dataViewLazy.id) { + if (dataViewLazy.id && dataViewLazy.isPersisted()) { return this.get(dataViewLazy.id); } @@ -1460,7 +1467,7 @@ export class DataViewsService { // unsaved DataView changes will not be reflected in the returned DataViewLazy async toDataViewLazy(dataView: DataView) { // if persisted - if (dataView.id) { + if (dataView.id && dataView.isPersisted()) { const dataViewLazy = await this.getDataViewLazy(dataView.id); return dataViewLazy!; } diff --git a/src/plugins/data_views/public/index.ts b/src/plugins/data_views/public/index.ts index f690552b5a147..973205b634e24 100644 --- a/src/plugins/data_views/public/index.ts +++ b/src/plugins/data_views/public/index.ts @@ -47,7 +47,7 @@ export type { DataViewsServicePublic, DataViewsServicePublicDeps, } from './data_views_service_public'; -export { DataViewsApiClient, DataViewsService, DataView } from './data_views'; +export { DataViewsApiClient, DataViewsService, DataView, DataViewLazy } from './data_views'; export type { DataViewListItem } from './data_views'; export { UiSettingsPublicToCommon } from './ui_settings_wrapper'; diff --git a/src/plugins/data_views/public/mocks.ts b/src/plugins/data_views/public/mocks.ts index 47b1eb8f8d2e3..d4492664adea9 100644 --- a/src/plugins/data_views/public/mocks.ts +++ b/src/plugins/data_views/public/mocks.ts @@ -41,6 +41,7 @@ const createStartContract = (): Start => { getFieldsForIndexPattern: jest.fn(), create: jest.fn().mockReturnValue(Promise.resolve({})), toDataView: jest.fn().mockReturnValue(Promise.resolve({})), + toDataViewLazy: jest.fn().mockReturnValue(Promise.resolve({})), } as unknown as jest.Mocked<DataViewsContract>; }; diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx index 85c2dd581eecb..458d3dcb6318c 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx @@ -114,15 +114,10 @@ describe('Discover documents layout', () => { }); test('should render customisations', async () => { - const customControlColumnsConfiguration = () => ({ - leadingControlColumns: [], - trailingControlColumns: [], - }); - const customization: DiscoverCustomization = { id: 'data_table', logsEnabled: true, - customControlColumnsConfiguration, + rowAdditionalLeadingControls: [], }; customisationService.set(customization); @@ -130,8 +125,8 @@ describe('Discover documents layout', () => { const discoverGridComponent = component.find(DiscoverGrid); expect(discoverGridComponent.exists()).toBeTruthy(); - expect(discoverGridComponent.prop('customControlColumnsConfiguration')).toEqual( - customControlColumnsConfiguration + expect(discoverGridComponent.prop('rowAdditionalLeadingControls')).toBe( + customization.rowAdditionalLeadingControls ); expect(discoverGridComponent.prop('externalCustomRenderers')).toBeDefined(); expect(discoverGridComponent.prop('customGridColumnsConfiguration')).toBeDefined(); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index e6fb6472397f3..e1b5636d010b1 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -259,7 +259,7 @@ function DiscoverDocumentsComponent({ [dataView, onAddColumn, onAddFilter, onRemoveColumn, query, savedSearch.id, setExpandedDoc] ); - const { customControlColumnsConfiguration } = useDiscoverCustomization('data_table') || {}; + const { rowAdditionalLeadingControls } = useDiscoverCustomization('data_table') || {}; const { customCellRenderer, customGridColumnsConfiguration } = useContextualGridCustomisations() || {}; const additionalFieldGroups = useAdditionalFieldGroups(); @@ -435,7 +435,7 @@ function DiscoverDocumentsComponent({ componentsTourSteps={TOUR_STEPS} externalCustomRenderers={cellRenderers} customGridColumnsConfiguration={customGridColumnsConfiguration} - customControlColumnsConfiguration={customControlColumnsConfiguration} + rowAdditionalLeadingControls={rowAdditionalLeadingControls} additionalFieldGroups={additionalFieldGroups} /> </CellActionsProvider> 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 2e9b6481b1081..79ef9fe6b3dc9 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 @@ -46,6 +46,7 @@ test('getTopNavLinks result', () => { "label": "Try ES|QL", "run": [Function], "testId": "select-text-based-language-btn", + "tooltip": "ES|QL is Elastic's powerful new piped query language.", }, Object { "description": "New Search", @@ -110,6 +111,7 @@ test('getTopNavLinks result for ES|QL mode', () => { "label": "Switch to classic", "run": [Function], "testId": "switch-to-dataviews", + "tooltip": "Switch to KQL or Lucene syntax.", }, Object { "description": "New Search", 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 a333bde02fd89..72cd9d71d12df 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 @@ -81,6 +81,13 @@ export const getTopNavLinks = ({ iconType: 'editorRedo', fill: false, color: 'text', + tooltip: isEsqlMode + ? i18n.translate('discover.localMenu.switchToClassicTooltipLabel', { + defaultMessage: 'Switch to KQL or Lucene syntax.', + }) + : i18n.translate('discover.localMenu.esqlTooltipLabel', { + defaultMessage: `ES|QL is Elastic's powerful new piped query language.`, + }), run: () => { if (dataView) { if (isEsqlMode) { diff --git a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx index 79ca8afd005e3..986f66e9680c4 100644 --- a/src/plugins/discover/public/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/components/discover_grid/discover_grid.tsx @@ -16,21 +16,33 @@ import { useProfileAccessor } from '../../context_awareness'; /** * Customized version of the UnifiedDataTable - * @param props * @constructor */ -export const DiscoverGrid: React.FC<UnifiedDataTableProps> = (props) => { +export const DiscoverGrid: React.FC<UnifiedDataTableProps> = ({ + rowAdditionalLeadingControls: customRowAdditionalLeadingControls, + ...props +}) => { const getRowIndicatorProvider = useProfileAccessor('getRowIndicatorProvider'); const getRowIndicator = useMemo(() => { return getRowIndicatorProvider(() => undefined)({ dataView: props.dataView }); }, [getRowIndicatorProvider, props.dataView]); + const getRowAdditionalLeadingControlsAccessor = useProfileAccessor( + 'getRowAdditionalLeadingControls' + ); + const rowAdditionalLeadingControls = useMemo(() => { + return getRowAdditionalLeadingControlsAccessor(() => customRowAdditionalLeadingControls)({ + dataView: props.dataView, + }); + }, [getRowAdditionalLeadingControlsAccessor, props.dataView, customRowAdditionalLeadingControls]); + return ( <UnifiedDataTable showColumnTokens enableComparisonMode renderCustomToolbar={renderCustomToolbar} getRowIndicator={getRowIndicator} + rowAdditionalLeadingControls={rowAdditionalLeadingControls} {...props} /> ); diff --git a/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx b/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx index 911e69e6d0d33..747bada5b0284 100644 --- a/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx +++ b/src/plugins/discover/public/context_awareness/profile_providers/example_data_source_profile/profile.tsx @@ -8,6 +8,7 @@ import { EuiBadge } from '@elastic/eui'; import type { DataTableRecord } from '@kbn/discover-utils'; +import type { RowControlColumn } from '@kbn/unified-data-table'; import { isOfAggregateQueryType } from '@kbn/es-query'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; import { euiThemeVars } from '@kbn/ui-theme'; @@ -71,6 +72,31 @@ export const exampleDataSourceProfileProvider: DataSourceProfileProvider = { }, }; }, + getRowAdditionalLeadingControls: (prev) => (params) => { + const additionalControls = prev(params) || []; + + return [ + ...additionalControls, + ...['visBarVerticalStacked', 'heart', 'inspect'].map( + (iconType, index): RowControlColumn => ({ + id: `exampleControl_${iconType}`, + headerAriaLabel: `Example Row Control ${iconType}`, + renderControl: (Control, rowProps) => { + return ( + <Control + data-test-subj={`exampleLogsControl_${iconType}`} + label={`Example ${iconType}`} + iconType={iconType} + onClick={() => { + alert(`Example "${iconType}" control clicked. Row index: ${rowProps.rowIndex}`); + }} + /> + ); + }, + }) + ), + ]; + }, getDefaultAppState: () => () => ({ columns: [ { diff --git a/src/plugins/discover/public/context_awareness/types.ts b/src/plugins/discover/public/context_awareness/types.ts index 38c7116a765b1..b6e4d4558162d 100644 --- a/src/plugins/discover/public/context_awareness/types.ts +++ b/src/plugins/discover/public/context_awareness/types.ts @@ -38,11 +38,20 @@ export interface DefaultAppStateExtension { rowHeight?: number; } +export interface RowControlsExtensionParams { + dataView: DataView; +} + export interface Profile { - getCellRenderers: () => CustomCellRenderer; - getDocViewer: (params: DocViewerExtensionParams) => DocViewerExtension; getDefaultAppState: (params: DefaultAppStateExtensionParams) => DefaultAppStateExtension; + // Data grid + getCellRenderers: () => CustomCellRenderer; getRowIndicatorProvider: ( params: RowIndicatorExtensionParams ) => UnifiedDataTableProps['getRowIndicator'] | undefined; + getRowAdditionalLeadingControls: ( + params: RowControlsExtensionParams + ) => UnifiedDataTableProps['rowAdditionalLeadingControls'] | undefined; + // Doc viewer + getDocViewer: (params: DocViewerExtensionParams) => DocViewerExtension; } diff --git a/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts b/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts index 3e6e510488fbd..d53485911d12c 100644 --- a/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts +++ b/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { CustomControlColumnConfiguration } from '@kbn/unified-data-table'; +import type { UnifiedDataTableProps } from '@kbn/unified-data-table'; export interface DataTableCustomization { id: 'data_table'; logsEnabled: boolean; // TODO / NOTE: Just temporary until Discover's data type contextual awareness lands. - customControlColumnsConfiguration?: CustomControlColumnConfiguration; + rowAdditionalLeadingControls?: UnifiedDataTableProps['rowAdditionalLeadingControls']; } diff --git a/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx b/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx index 2c67595b846b6..77d6f44de126b 100644 --- a/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx +++ b/src/plugins/discover/public/embeddable/components/saved_search_grid.tsx @@ -9,7 +9,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import { AggregateQuery, Query } from '@kbn/es-query'; import type { SearchResponseWarning } from '@kbn/search-response-warnings'; -import { MAX_DOC_FIELDS_DISPLAYED, ROW_HEIGHT_OPTION, SHOW_MULTIFIELDS } from '@kbn/discover-utils'; +import { MAX_DOC_FIELDS_DISPLAYED, SHOW_MULTIFIELDS } from '@kbn/discover-utils'; import { type UnifiedDataTableProps, type DataTableColumnsMeta, @@ -108,7 +108,6 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) { totalHits={props.totalHitCount} setExpandedDoc={setExpandedDoc} expandedDoc={expandedDoc} - configRowHeight={props.services.uiSettings.get(ROW_HEIGHT_OPTION)} showMultiFields={props.services.uiSettings.get(SHOW_MULTIFIELDS)} maxDocFieldsDisplayed={props.services.uiSettings.get(MAX_DOC_FIELDS_DISPLAYED)} renderDocumentView={renderDocumentView} @@ -116,7 +115,6 @@ export function DiscoverGridEmbeddable(props: DiscoverGridEmbeddableProps) { externalCustomRenderers={cellRenderers} enableComparisonMode showColumnTokens - configHeaderRowHeight={3} showFullScreenButton={false} className="unifiedDataTable" /> diff --git a/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx b/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx index fe511f5887dd5..55ed59d30f1ae 100644 --- a/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx +++ b/src/plugins/discover/public/embeddable/components/search_embeddable_grid_component.tsx @@ -12,8 +12,8 @@ import { BehaviorSubject } from 'rxjs'; import type { DataView } from '@kbn/data-views-plugin/common'; import { DOC_HIDE_TIME_COLUMN_SETTING, - isLegacyTableEnabled, SEARCH_FIELDS_FROM_SOURCE, + isLegacyTableEnabled, } from '@kbn/discover-utils'; import { Filter } from '@kbn/es-query'; import { @@ -33,6 +33,7 @@ import { SEARCH_EMBEDDABLE_CELL_ACTIONS_TRIGGER_ID } from '../constants'; import { isEsqlMode } from '../initialize_fetch'; import type { SearchEmbeddableApi, SearchEmbeddableStateManager } from '../types'; import { DiscoverGridEmbeddable } from './saved_search_grid'; +import { getSearchEmbeddableDefaults } from '../get_search_embeddable_defaults'; interface SavedSearchEmbeddableComponentProps { api: SearchEmbeddableApi & { fetchWarnings$: BehaviorSubject<SearchResponseIncompleteWarning[]> }; @@ -144,13 +145,15 @@ export function SearchEmbeddableGridComponent({ return getAllowedSampleSize(savedSearch.sampleSize, discoverServices.uiSettings); }, [savedSearch.sampleSize, discoverServices]); + const defaults = getSearchEmbeddableDefaults(discoverServices.uiSettings); + const sharedProps = { columns: savedSearch.columns ?? [], dataView, interceptedWarnings, onFilter: onAddFilter, rows, - rowsPerPageState: savedSearch.rowsPerPage, + rowsPerPageState: savedSearch.rowsPerPage ?? defaults.rowsPerPage, sampleSizeState: fetchedSampleSize, searchDescription: panelDescription || savedSearchDescription, sort, @@ -179,12 +182,14 @@ export function SearchEmbeddableGridComponent({ ariaLabelledBy={'documentsAriaLabel'} cellActionsTriggerId={SEARCH_EMBEDDABLE_CELL_ACTIONS_TRIGGER_ID} columnsMeta={columnsMeta} + configHeaderRowHeight={defaults.headerRowHeight} + configRowHeight={defaults.rowHeight} headerRowHeightState={savedSearch.headerRowHeight} + rowHeightState={savedSearch.rowHeight} isPlainRecord={isEsql} loadingState={Boolean(loading) ? DataLoadingState.loading : DataLoadingState.loaded} maxAllowedSampleSize={getMaxAllowedSampleSize(discoverServices.uiSettings)} query={savedSearch.searchSource.getField('query')} - rowHeightState={savedSearch.rowHeight} savedSearchId={savedSearchId} searchTitle={panelTitle || savedSearchTitle} services={discoverServices} diff --git a/src/plugins/discover/public/embeddable/get_search_embeddable_defaults.ts b/src/plugins/discover/public/embeddable/get_search_embeddable_defaults.ts new file mode 100644 index 0000000000000..e820f2ec38e21 --- /dev/null +++ b/src/plugins/discover/public/embeddable/get_search_embeddable_defaults.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { IUiSettingsClient } from '@kbn/core/public'; +import { ROW_HEIGHT_OPTION, SAMPLE_SIZE_SETTING } from '@kbn/discover-utils'; +import { getDefaultRowsPerPage } from '../../common/constants'; +import { DEFAULT_HEADER_ROW_HEIGHT_LINES } from './constants'; + +export interface SearchEmbeddableDefaults { + rowHeight: number | undefined; + headerRowHeight: number | undefined; + rowsPerPage: number | undefined; + sampleSize: number | undefined; +} + +export const getSearchEmbeddableDefaults = ( + uiSettings: IUiSettingsClient +): SearchEmbeddableDefaults => { + return { + rowHeight: uiSettings.get(ROW_HEIGHT_OPTION), + headerRowHeight: DEFAULT_HEADER_ROW_HEIGHT_LINES, + rowsPerPage: getDefaultRowsPerPage(uiSettings), + sampleSize: uiSettings.get(SAMPLE_SIZE_SETTING), + }; +}; diff --git a/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx b/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx index 02f64d34fac2c..cc6711e3f5015 100644 --- a/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx +++ b/src/plugins/discover/public/embeddable/initialize_search_embeddable_api.tsx @@ -12,7 +12,6 @@ import { BehaviorSubject, combineLatest, map, Observable, skip } from 'rxjs'; import { ISearchSource, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { DataView } from '@kbn/data-views-plugin/common'; -import { ROW_HEIGHT_OPTION, SAMPLE_SIZE_SETTING } from '@kbn/discover-utils'; import { DataTableRecord } from '@kbn/discover-utils/types'; import type { PublishesDataViews, @@ -24,9 +23,9 @@ import { SortOrder, VIEW_MODE } from '@kbn/saved-search-plugin/public'; import { DataTableColumnsMeta } from '@kbn/unified-data-table'; import { AggregateQuery, Filter, Query } from '@kbn/es-query'; -import { getDefaultRowsPerPage } from '../../common/constants'; import { DiscoverServices } from '../build_services'; -import { DEFAULT_HEADER_ROW_HEIGHT_LINES, EDITABLE_SAVED_SEARCH_KEYS } from './constants'; +import { EDITABLE_SAVED_SEARCH_KEYS } from './constants'; +import { getSearchEmbeddableDefaults } from './get_search_embeddable_defaults'; import { PublishesSavedSearch, SearchEmbeddableRuntimeState, @@ -85,14 +84,16 @@ export const initializeSearchEmbeddableApi = async ( const searchSource$ = new BehaviorSubject<ISearchSource>(searchSource); const dataViews = new BehaviorSubject<DataView[] | undefined>(dataView ? [dataView] : undefined); + const defaults = getSearchEmbeddableDefaults(discoverServices.uiSettings); + /** This is the state that can be initialized from the saved initial state */ const columns$ = new BehaviorSubject<string[] | undefined>(initialState.columns); const grid$ = new BehaviorSubject<DiscoverGridSettings | undefined>(initialState.grid); + const headerRowHeight$ = new BehaviorSubject<number | undefined>(initialState.headerRowHeight); const rowHeight$ = new BehaviorSubject<number | undefined>(initialState.rowHeight); const rowsPerPage$ = new BehaviorSubject<number | undefined>(initialState.rowsPerPage); - const headerRowHeight$ = new BehaviorSubject<number | undefined>(initialState.headerRowHeight); - const sort$ = new BehaviorSubject<SortOrder[] | undefined>(initialState.sort); const sampleSize$ = new BehaviorSubject<number | undefined>(initialState.sampleSize); + const sort$ = new BehaviorSubject<SortOrder[] | undefined>(initialState.sort); const savedSearchViewMode$ = new BehaviorSubject<VIEW_MODE | undefined>(initialState.viewMode); /** @@ -112,10 +113,6 @@ export const initializeSearchEmbeddableApi = async ( const columnsMeta$ = new BehaviorSubject<DataTableColumnsMeta | undefined>(undefined); const totalHitCount$ = new BehaviorSubject<number | undefined>(undefined); - const defaultRowHeight = discoverServices.uiSettings.get(ROW_HEIGHT_OPTION); - const defaultRowsPerPage = getDefaultRowsPerPage(discoverServices.uiSettings); - const defaultSampleSize = discoverServices.uiSettings.get(SAMPLE_SIZE_SETTING); - /** * The state manager is used to modify the state of the saved search - this should never be * treated as the source of truth @@ -175,22 +172,22 @@ export const initializeSearchEmbeddableApi = async ( sampleSize: [ sampleSize$, (value) => sampleSize$.next(value), - (a, b) => (a ?? defaultSampleSize) === (b ?? defaultSampleSize), + (a, b) => (a ?? defaults.sampleSize) === (b ?? defaults.sampleSize), ], rowsPerPage: [ rowsPerPage$, (value) => rowsPerPage$.next(value), - (a, b) => (a ?? defaultRowsPerPage) === (b ?? defaultRowsPerPage), + (a, b) => (a ?? defaults.rowsPerPage) === (b ?? defaults.rowsPerPage), ], rowHeight: [ rowHeight$, (value) => rowHeight$.next(value), - (a, b) => (a ?? defaultRowHeight) === (b ?? defaultRowHeight), + (a, b) => (a ?? defaults.rowHeight) === (b ?? defaults.rowHeight), ], headerRowHeight: [ headerRowHeight$, (value) => headerRowHeight$.next(value), - (a, b) => (a ?? DEFAULT_HEADER_ROW_HEIGHT_LINES) === (b ?? DEFAULT_HEADER_ROW_HEIGHT_LINES), + (a, b) => (a ?? defaults.headerRowHeight) === (b ?? defaults.headerRowHeight), ], /** The following can't currently be changed from the dashboard */ diff --git a/src/plugins/embeddable/public/react_embeddable_system/types.ts b/src/plugins/embeddable/public/react_embeddable_system/types.ts index 8973cef9ce109..5860737b13fc8 100644 --- a/src/plugins/embeddable/public/react_embeddable_system/types.ts +++ b/src/plugins/embeddable/public/react_embeddable_system/types.ts @@ -31,7 +31,7 @@ export interface DefaultEmbeddableApi< > extends DefaultPresentationPanelApi, HasType, PublishesPhaseEvents, - PublishesUnsavedChanges, + Partial<PublishesUnsavedChanges>, HasSerializableState<SerializedState>, HasSnapshottableState<RuntimeState> {} diff --git a/test/api_integration/apis/console/index.ts b/test/api_integration/apis/console/index.ts index f39cf6cabb129..4e4d97b8204c8 100644 --- a/test/api_integration/apis/console/index.ts +++ b/test/api_integration/apis/console/index.ts @@ -13,6 +13,5 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./proxy_route')); loadTestFile(require.resolve('./autocomplete_entities')); loadTestFile(require.resolve('./es_config')); - loadTestFile(require.resolve('./spec_definitions')); }); } diff --git a/test/api_integration/apis/console/spec_definitions.ts b/test/api_integration/apis/console/spec_definitions.ts deleted file mode 100644 index f8e56354f6319..0000000000000 --- a/test/api_integration/apis/console/spec_definitions.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - describe('GET /api/console/api_server', () => { - it('returns autocomplete definitions', async () => { - const { body } = await supertest - .get('/api/console/api_server') - .set('kbn-xsrf', 'true') - .expect(200); - expect(body.es).to.be.ok(); - const { - es: { name, globals, endpoints }, - } = body; - expect(name).to.be.ok(); - expect(Object.keys(globals).length).to.be.above(0); - expect(Object.keys(endpoints).length).to.be.above(0); - }); - }); -} diff --git a/test/api_integration/apis/core/compression.ts b/test/api_integration/apis/core/compression.ts deleted file mode 100644 index c4b119692f4bb..0000000000000 --- a/test/api_integration/apis/core/compression.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 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - const compressionSuite = (url: string) => { - it(`uses compression when there isn't a referer`, async () => { - await supertest - .get(url) - .set('accept-encoding', 'gzip') - .then((response) => { - expect(response.header).to.have.property('content-encoding', 'gzip'); - }); - }); - - it(`uses compression when there is a whitelisted referer`, async () => { - await supertest - .get(url) - .set('accept-encoding', 'gzip') - .set('referer', 'https://some-host.com') - .then((response) => { - expect(response.header).to.have.property('content-encoding', 'gzip'); - }); - }); - - it(`doesn't use compression when there is a non-whitelisted referer`, async () => { - await supertest - .get(url) - .set('accept-encoding', 'gzip') - .set('referer', 'https://other.some-host.com') - .then((response) => { - expect(response.header).not.to.have.property('content-encoding'); - }); - }); - - it(`supports brotli compression`, async () => { - await supertest - .get(url) - .set('accept-encoding', 'br') - .then((response) => { - expect(response.header).to.have.property('content-encoding', 'br'); - }); - }); - }; - - describe('compression', () => { - describe('against an application page', () => { - compressionSuite('/app/kibana'); - }); - }); -} diff --git a/test/api_integration/apis/core/index.ts b/test/api_integration/apis/core/index.ts index 65c8a5d5cb5c5..98010e01c8215 100644 --- a/test/api_integration/apis/core/index.ts +++ b/test/api_integration/apis/core/index.ts @@ -10,7 +10,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('core', () => { - loadTestFile(require.resolve('./compression')); loadTestFile(require.resolve('./translations')); loadTestFile(require.resolve('./capabilities')); }); diff --git a/test/api_integration/apis/esql/errors.ts b/test/api_integration/apis/esql/errors.ts index 193d48c478209..bf2d7e2139a91 100644 --- a/test/api_integration/apis/esql/errors.ts +++ b/test/api_integration/apis/esql/errors.ts @@ -189,7 +189,12 @@ export default function ({ getService }: FtrProviderContext) { const fieldsExcludingCounterType = config.fields.filter( // ES|QL supports counter_integer, counter_long, counter_double, date_period, etc. // but they are not types suitable for Elasticsearch indices - (c: { type: string }) => !c.type.startsWith('counter_') && c.type !== 'date_period' + (c: { type: string }) => + !c.type.startsWith('counter_') && + c.type !== 'date_period' && + c.type !== 'time_duration' && + c.type !== 'null' && + c.type !== 'time_literal' ); await es.indices.create( createIndexRequest( diff --git a/test/common/services/index.ts b/test/common/services/index.ts index ad08829afd047..ccc786d4ccc6e 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -16,8 +16,15 @@ import { IndexPatternsService } from './index_patterns'; import { BsearchService } from './bsearch'; import { ConsoleProvider } from './console'; +// pick only services that work for any FTR config, e.g. 'samlAuth' requires SAML setup in config file +const { es, esArchiver, kibanaServer, retry, supertestWithoutAuth } = commonFunctionalServices; + export const services = { - ...commonFunctionalServices, + es, + esArchiver, + kibanaServer, + retry, + supertestWithoutAuth, deployment: DeploymentService, randomness: RandomnessService, security: SecurityServiceProvider, diff --git a/test/functional/apps/discover/context_awareness/extensions/_get_row_additional_leading_controls.ts b/test/functional/apps/discover/context_awareness/extensions/_get_row_additional_leading_controls.ts new file mode 100644 index 0000000000000..be536fd6cdbe9 --- /dev/null +++ b/test/functional/apps/discover/context_awareness/extensions/_get_row_additional_leading_controls.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import kbnRison from '@kbn/rison'; +import type { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'discover']); + const testSubjects = getService('testSubjects'); + const dataViews = getService('dataViews'); + + describe('extension getRowAdditionalLeadingControls', () => { + describe('ES|QL mode', () => { + it('should render logs controls for logs data source', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-logs | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + + it('should not render logs controls for non-logs data source', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-metrics | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + }); + + describe('data view mode', () => { + it('should render logs controls for logs data source', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-logs'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + + it('should not render logs controls for non-logs data source', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-metrics'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + }); + }); +} diff --git a/test/functional/apps/discover/context_awareness/index.ts b/test/functional/apps/discover/context_awareness/index.ts index 82f03e7f54bbc..0bba18a339263 100644 --- a/test/functional/apps/discover/context_awareness/index.ts +++ b/test/functional/apps/discover/context_awareness/index.ts @@ -36,6 +36,7 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid loadTestFile(require.resolve('./_root_profile')); loadTestFile(require.resolve('./_data_source_profile')); loadTestFile(require.resolve('./extensions/_get_row_indicator_provider')); + loadTestFile(require.resolve('./extensions/_get_row_additional_leading_controls')); loadTestFile(require.resolve('./extensions/_get_doc_viewer')); loadTestFile(require.resolve('./extensions/_get_cell_renderers')); loadTestFile(require.resolve('./extensions/_get_default_app_state')); diff --git a/test/functional/apps/discover/esql/_esql_view.ts b/test/functional/apps/discover/esql/_esql_view.ts index a66471b921528..0ab0e30a45eab 100644 --- a/test/functional/apps/discover/esql/_esql_view.ts +++ b/test/functional/apps/discover/esql/_esql_view.ts @@ -194,8 +194,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const cell = await dataGrid.getCellElementExcludingControlColumns(0, 1); expect(await cell.getVisibleText()).to.be(' - '); expect(await dataGrid.getHeaders()).to.eql([ - 'Control column', 'Select column', + 'Control column', 'Numberbytes', 'machine.ram_range', ]); diff --git a/test/functional/apps/discover/group1/_discover.ts b/test/functional/apps/discover/group1/_discover.ts index c931187250cc3..e6cf2edca620f 100644 --- a/test/functional/apps/discover/group1/_discover.ts +++ b/test/functional/apps/discover/group1/_discover.ts @@ -216,8 +216,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.uiSettings.update({ 'dateFormat:tz': 'America/Phoenix' }); await PageObjects.common.navigateToApp('discover'); await PageObjects.header.awaitKibanaChrome(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.timePicker.setDefaultAbsoluteRange(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); await queryBar.clearQuery(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); log.debug( 'check that the newest doc timestamp is now -7 hours from the UTC time in the first test' diff --git a/test/functional/apps/discover/group2_data_grid3/_data_grid_pagination.ts b/test/functional/apps/discover/group2_data_grid3/_data_grid_pagination.ts index 193d3f4607987..55747eec93119 100644 --- a/test/functional/apps/discover/group2_data_grid3/_data_grid_pagination.ts +++ b/test/functional/apps/discover/group2_data_grid3/_data_grid_pagination.ts @@ -30,6 +30,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const security = getService('security'); const dashboardAddPanel = getService('dashboardAddPanel'); + const dashboardPanelActions = getService('dashboardPanelActions'); describe('discover data grid pagination', function describeIndexTests() { before(async () => { @@ -126,6 +127,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); expect((await dataGrid.getDocTableRows()).length).to.be(10); // as in the saved search await dataGrid.checkCurrentRowsPerPageToBe(10); + + // should use "rowsPerPage" form the saved search on dashboard + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + await dashboardAddPanel.clickOpenAddPanel(); + await dashboardAddPanel.addSavedSearch(savedSearchTitle); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect((await dataGrid.getDocTableRows()).length).to.be(10); // as in the saved search + await dataGrid.checkCurrentRowsPerPageToBe(10); + + // should use "rowsPerPage" form settings by default on dashboard + await dashboardPanelActions.removePanelByTitle(savedSearchTitle); + await dashboardAddPanel.addSavedSearch('A Saved Search'); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect((await dataGrid.getDocTableRows()).length).to.be(6); // as in settings + await dataGrid.checkCurrentRowsPerPageToBe(6); }); it('should not split ES|QL results into pages', async () => { diff --git a/test/functional/apps/home/_welcome.ts b/test/functional/apps/home/_welcome.ts index 6c8bda90de699..d61afd879090e 100644 --- a/test/functional/apps/home/_welcome.ts +++ b/test/functional/apps/home/_welcome.ts @@ -15,8 +15,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'home']); const deployment = getService('deployment'); - // FLAKY: https://github.com/elastic/kibana/issues/186168 - describe.skip('Welcome interstitial', () => { + describe('Welcome interstitial', () => { beforeEach(async () => { // Need to navigate to page first to clear storage before test can be run await PageObjects.common.navigateToUrl('home', undefined); diff --git a/test/functional/apps/management/data_views/_field_formatter.ts b/test/functional/apps/management/data_views/_field_formatter.ts index 9754bacee599f..7cfa792c41332 100644 --- a/test/functional/apps/management/data_views/_field_formatter.ts +++ b/test/functional/apps/management/data_views/_field_formatter.ts @@ -549,7 +549,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.settings.setFieldFormat(spec.applyFormatterType); if (spec.beforeSave) { - await spec.beforeSave(await testSubjects.find('formatRow')); + await spec.beforeSave(); } }); }); @@ -604,7 +604,7 @@ interface FieldFormatEditorSpecDescriptor { * Use it set specific configuration params for applied field formatter * @param formatRowContainer - field format editor container */ - beforeSave?: (formatRowContainer: WebElementWrapper) => Promise<void>; + beforeSave?: () => Promise<void>; /** * An expected formatted value rendered by Discover app, diff --git a/test/functional/apps/visualize/group3/_annotation_listing.ts b/test/functional/apps/visualize/group3/_annotation_listing.ts index 541fc6fb5538e..f58f2fd386028 100644 --- a/test/functional/apps/visualize/group3/_annotation_listing.ts +++ b/test/functional/apps/visualize/group3/_annotation_listing.ts @@ -18,8 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const log = getService('log'); - // Failing: See https://github.com/elastic/kibana/issues/168281 - describe.skip('annotation listing page', function () { + describe('annotation listing page', function () { before(async function () { await kibanaServer.importExport.load( 'test/functional/fixtures/kbn_archiver/annotation_listing_page_search' @@ -49,8 +48,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await listingTable.clearSearchFilter(); }); - // FLAKY: https://github.com/elastic/kibana/issues/168281 - describe.skip('by text', () => { + describe('by text', () => { it('matches on the first word', async function () { await retry.try(async () => { await listingTable.searchForItemWithName('search'); diff --git a/test/functional/page_objects/home_page.ts b/test/functional/page_objects/home_page.ts index 0c85c51381b94..7dd5ee171afe6 100644 --- a/test/functional/page_objects/home_page.ts +++ b/test/functional/page_objects/home_page.ts @@ -57,9 +57,14 @@ export class HomePageObject extends FtrService { } async isWelcomeInterstitialDisplayed() { - // give the interstitial enough time to fade in - await new Promise((resolve) => setTimeout(resolve, 500)); - return await this.testSubjects.isDisplayed('homeWelcomeInterstitial', 2000); + // This element inherits style defined {@link https://github.com/elastic/kibana/blob/v8.14.3/src/core/public/styles/core_app/_mixins.scss#L72|here} + // with an animation duration set to $euiAnimSpeedExtraSlow {@see https://eui.elastic.co/#/theming/more-tokens#animation}, + // hence we setup a delay so the interstitial has enough time to fade in + const animSpeedExtraSlow = 500; + await new Promise((resolve) => setTimeout(resolve, animSpeedExtraSlow)); + return this.retry.try(async () => { + return await this.testSubjects.isDisplayed('homeWelcomeInterstitial', animSpeedExtraSlow * 4); + }); } async isGuidedOnboardingLandingDisplayed() { diff --git a/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png b/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png index 3fd3025ebb9a1..94de1a1c3cc4f 100644 Binary files a/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png and b/test/functional/screenshots/baseline/dashboard_embed_mode_scrolling.png differ diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts index 5014846a67407..70a67d33ffd00 100644 --- a/test/functional/services/data_grid.ts +++ b/test/functional/services/data_grid.ts @@ -139,6 +139,22 @@ export class DataGridService extends FtrService { 'euiDataGridCellExpandButton' ); await actionButton.click(); + await this.retry.waitFor('popover to be opened', async () => { + return await this.testSubjects.exists('euiDataGridExpansionPopover'); + }); + } + + /** + * Clicks grid cell 'expand' action button + * @param rowIndex data row index starting from 0 (0 means 1st row) + * @param columnIndex column index starting from 0 (0 means 1st column) + */ + public async clickCellExpandButtonExcludingControlColumns( + rowIndex: number = 0, + columnIndex: number = 0 + ) { + const controlsCount = await this.getControlColumnsCount(); + await this.clickCellExpandButton(rowIndex, controlsCount + columnIndex); } /** diff --git a/test/functional/services/listing_table.ts b/test/functional/services/listing_table.ts index 9a3e6580db0e9..a3a056358fa24 100644 --- a/test/functional/services/listing_table.ts +++ b/test/functional/services/listing_table.ts @@ -85,7 +85,7 @@ export class ListingTableService extends FtrService { } public async waitUntilTableIsLoaded() { - return this.retry.try(async () => { + await this.retry.try(async () => { const isLoaded = await this.find.existsByDisplayedByCssSelector( '[data-test-subj="itemsInMemTable"]:not(.euiBasicTable-loading)' ); @@ -264,6 +264,10 @@ export class ListingTableService extends FtrService { await searchFilter.type(name); await this.common.pressEnterKey(); + const filterValue = await this.getSearchFilterValue(); + if (filterValue !== name) { + throw new Error(`the input value has not updated properly`); + } }); await this.waitUntilTableIsLoaded(); diff --git a/test/plugin_functional/test_suites/saved_objects_management/hidden_from_http_apis.ts b/test/plugin_functional/test_suites/saved_objects_management/hidden_from_http_apis.ts index c37bd671a764f..3bb078d4d6864 100644 --- a/test/plugin_functional/test_suites/saved_objects_management/hidden_from_http_apis.ts +++ b/test/plugin_functional/test_suites/saved_objects_management/hidden_from_http_apis.ts @@ -12,6 +12,11 @@ import type { Response } from 'supertest'; import { SavedObject } from '@kbn/core/types'; import type { PluginFunctionalProviderContext } from '../../services'; +interface MinimalSO { + id: string; + type: string; +} + function parseNdJson(input: string): Array<SavedObject<any>> { return input.split('\n').map((str) => JSON.parse(str)); } @@ -112,10 +117,12 @@ export default function ({ getService }: PluginFunctionalProviderContext) { .expect(200) .then((resp) => { expect( - resp.body.saved_objects.map((so: { id: string; type: string }) => ({ - id: so.id, - type: so.type, - })) + resp.body.saved_objects + .map((so: MinimalSO) => ({ + id: so.id, + type: so.type, + })) + .sort((a: MinimalSO, b: MinimalSO) => (a.id > b.id ? 1 : -1)) ).to.eql([ { id: 'hidden-from-http-apis-1', diff --git a/tsconfig.base.json b/tsconfig.base.json index 477e52bc568eb..d4b0d76a56413 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -94,8 +94,6 @@ "@kbn/app-link-test-plugin/*": ["test/plugin_functional/plugins/app_link_test/*"], "@kbn/application-usage-test-plugin": ["x-pack/test/usage_collection/plugins/application_usage_test"], "@kbn/application-usage-test-plugin/*": ["x-pack/test/usage_collection/plugins/application_usage_test/*"], - "@kbn/assets-data-access-plugin": ["x-pack/plugins/observability_solution/assets_data_access"], - "@kbn/assets-data-access-plugin/*": ["x-pack/plugins/observability_solution/assets_data_access/*"], "@kbn/audit-log-plugin": ["x-pack/test/security_api_integration/plugins/audit_log"], "@kbn/audit-log-plugin/*": ["x-pack/test/security_api_integration/plugins/audit_log/*"], "@kbn/avc-banner": ["packages/kbn-avc-banner"], @@ -778,6 +776,8 @@ "@kbn/encrypted-saved-objects-plugin/*": ["x-pack/plugins/encrypted_saved_objects/*"], "@kbn/enterprise-search-plugin": ["x-pack/plugins/enterprise_search"], "@kbn/enterprise-search-plugin/*": ["x-pack/plugins/enterprise_search/*"], + "@kbn/entities-data-access-plugin": ["x-pack/plugins/observability_solution/entities_data_access"], + "@kbn/entities-data-access-plugin/*": ["x-pack/plugins/observability_solution/entities_data_access/*"], "@kbn/entities-schema": ["x-pack/packages/kbn-entities-schema"], "@kbn/entities-schema/*": ["x-pack/packages/kbn-entities-schema/*"], "@kbn/entityManager-plugin": ["x-pack/plugins/observability_solution/entity_manager"], diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.test.tsx index 0eb9d0a916c44..4d6b80b3e60a4 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.test.tsx @@ -7,9 +7,10 @@ import React from 'react'; import { ConnectorSelector } from '.'; -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import { TestProviders } from '../../mock/test_providers/test_providers'; import { mockActionTypes, mockConnectors } from '../../mock/connectors'; +import * as i18n from '../translations'; const onConnectorSelectionChange = jest.fn(); const setIsOpen = jest.fn(); @@ -107,4 +108,28 @@ describe('Connector selector', () => { expect(mockRefetchConnectors).toHaveBeenCalled(); expect(setIsOpen).toHaveBeenCalledWith(false); }); + + it('renders the expected placeholder when selectedConnectorId is undefined', () => { + render( + <TestProviders> + <ConnectorSelector {...defaultProps} selectedConnectorId={undefined} /> + </TestProviders> + ); + + expect(screen.getByTestId('connector-selector')).toHaveTextContent( + i18n.INLINE_CONNECTOR_PLACEHOLDER + ); + }); + + it('does NOT render the placeholder when selectedConnectorId is defined', () => { + render( + <TestProviders> + <ConnectorSelector {...defaultProps} /> + </TestProviders> + ); + + expect(screen.getByTestId('connector-selector')).not.toHaveTextContent( + i18n.INLINE_CONNECTOR_PLACEHOLDER + ); + }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx index ad0fffc44e6b5..12cb0a30c0c9c 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/connector_selector/index.tsx @@ -6,11 +6,13 @@ */ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSuperSelect, EuiText } from '@elastic/eui'; +import { css } from '@emotion/css'; import React, { Suspense, useCallback, useMemo, useState } from 'react'; import { ActionConnector, ActionType } from '@kbn/triggers-actions-ui-plugin/public'; import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/openai/constants'; +import { euiThemeVars } from '@kbn/ui-theme'; import { some } from 'lodash'; import type { AttackDiscoveryStats } from '@kbn/elastic-assistant-common'; import { AttackDiscoveryStatusIndicator } from './attack_discovery_status_indicator'; @@ -23,6 +25,13 @@ import { AddConnectorModal } from '../add_connector_modal'; export const ADD_NEW_CONNECTOR = 'ADD_NEW_CONNECTOR'; +const placeholderCss = css` + .euiSuperSelectControl__placeholder { + color: ${euiThemeVars.euiColorPrimary}; + margin-right: ${euiThemeVars.euiSizeXS}; + } +`; + interface Props { isDisabled?: boolean; isOpen?: boolean; @@ -187,6 +196,7 @@ export const ConnectorSelector: React.FC<Props> = React.memo( ) : ( <EuiSuperSelect aria-label={i18n.CONNECTOR_SELECTOR_TITLE} + className={placeholderCss} compressed={true} data-test-subj="connector-selector" disabled={localIsDisabled} @@ -195,6 +205,7 @@ export const ConnectorSelector: React.FC<Props> = React.memo( onChange={onChange} options={allConnectorOptions} valueOfSelected={selectedConnectorId} + placeholder={i18n.INLINE_CONNECTOR_PLACEHOLDER} popoverProps={{ panelMinWidth: 400, anchorPosition: 'downRight' }} /> )} diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts index 11d214afc4faf..c5642e54188d3 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/translations.ts @@ -52,7 +52,7 @@ export const ADD_CONNECTOR = i18n.translate( export const INLINE_CONNECTOR_PLACEHOLDER = i18n.translate( 'xpack.elasticAssistant.assistant.connectors.connectorSelectorInline.connectorPlaceholder', { - defaultMessage: 'Select a Connector', + defaultMessage: 'Select a connector', } ); diff --git a/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts b/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts index efb6eb2c3e23f..c3556803745a7 100644 --- a/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts +++ b/x-pack/packages/ml/aiops_log_pattern_analysis/create_category_request.ts @@ -112,11 +112,11 @@ export function createCategoryRequest( return { params: { index, - size: 0, body: { query, aggs: wrap(aggs), ...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}), + size: 0, }, }, }; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_categories_result.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_categories_result.ts new file mode 100644 index 0000000000000..4b233c4d67bce --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_categories_result.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const topCategoriesResultMock = [ + { + bg_count: 0, + doc_count: 1642, + fieldName: 'message', + fieldValue: + '71.231.222.196 - - [2018-08-13T05:04:08.731Z] "GET /kibana/kibana-6.3.2-windows-x86_64.zip HTTP/1.1" 200 15139 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"', + key: 'GET HTTP/1.1 Mozilla/5.0 X11 Linux x86_64 rv Gecko/20110421 Firefox/6.0a1', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'log_pattern', + }, + { + bg_count: 0, + doc_count: 1488, + fieldName: 'message', + fieldValue: + '7.210.210.41 - - [2018-08-13T04:20:49.558Z] "GET /elasticsearch/elasticsearch-6.3.2.deb HTTP/1.1" 404 6699 "-" "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24"', + key: 'GET HTTP/1.1 Mozilla/5.0 X11 Linux i686 AppleWebKit/534.24 KHTML like Gecko Chrome/11.0.696.50 Safari/534.24', + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'log_pattern', + }, +]; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_categories_search_response.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_categories_search_response.ts new file mode 100644 index 0000000000000..4114620a1b5c6 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_categories_search_response.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. + */ + +export const topCategoriesSearchResponseMock = { + took: 98, + responses: [ + { + took: 98, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 4413, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + categories: { + buckets: [ + { + doc_count: 1642, + key: 'GET HTTP/1.1 Mozilla/5.0 X11 Linux x86_64 rv Gecko/20110421 Firefox/6.0a1', + regex: + '.*?GET.+?HTTP/1\\.1.+?Mozilla/5\\.0.+?X11.+?Linux.+?x86_64.+?rv.+?Gecko/20110421.+?Firefox/6\\.0a1.*?', + max_matching_length: 233, + examples: { + hits: { + total: { value: 1642, relation: 'eq' }, + max_score: null, + hits: [ + { + _index: '.ds-kibana_sample_data_logs-2024.07.08-000001', + _id: 'zpkLk5AB4oRN3GwDmOW1', + _score: null, + _source: { + message: + '71.231.222.196 - - [2018-08-13T05:04:08.731Z] "GET /kibana/kibana-6.3.2-windows-x86_64.zip HTTP/1.1" 200 15139 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1"', + }, + sort: [1721624648731], + }, + ], + }, + }, + }, + { + doc_count: 1488, + key: 'GET HTTP/1.1 Mozilla/5.0 X11 Linux i686 AppleWebKit/534.24 KHTML like Gecko Chrome/11.0.696.50 Safari/534.24', + regex: + '.*?GET.+?HTTP/1\\.1.+?Mozilla/5\\.0.+?X11.+?Linux.+?i686.+?AppleWebKit/534\\.24.+?KHTML.+?like.+?Gecko.+?Chrome/11\\.0\\.696\\.50.+?Safari/534\\.24.*?', + max_matching_length: 266, + examples: { + hits: { + total: { value: 1488, relation: 'eq' }, + max_score: null, + hits: [ + { + _index: '.ds-kibana_sample_data_logs-2024.07.08-000001', + _id: 'VpkLk5AB4oRN3GwDmOW1', + _score: null, + _source: { + message: + '7.210.210.41 - - [2018-08-13T04:20:49.558Z] "GET /elasticsearch/elasticsearch-6.3.2.deb HTTP/1.1" 404 6699 "-" "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24"', + }, + sort: [1721622049558], + }, + ], + }, + }, + }, + ], + }, + }, + status: 200, + }, + ], +}; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_terms_result.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_terms_result.ts new file mode 100644 index 0000000000000..e3290941646b0 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_terms_result.ts @@ -0,0 +1,1311 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 topTermsResult = [ + { + key: 'agent.keyword:Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24', + type: 'keyword', + fieldName: 'agent.keyword', + fieldValue: + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24', + doc_count: 179, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'agent.keyword:Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1', + type: 'keyword', + fieldName: 'agent.keyword', + fieldValue: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1', + doc_count: 87, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'agent.keyword:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)', + type: 'keyword', + fieldName: 'agent.keyword', + fieldValue: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)', + doc_count: 63, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:30.156.16.164', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '30.156.16.164', + doc_count: 100, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:107.152.89.90', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '107.152.89.90', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:112.106.69.227', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '112.106.69.227', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:160.20.100.193', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '160.20.100.193', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:186.153.168.71', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '186.153.168.71', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:16.241.165.21', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '16.241.165.21', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:20.129.3.8', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '20.129.3.8', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:24.42.142.201', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '24.42.142.201', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:43.86.71.5', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '43.86.71.5', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'clientip:50.184.59.162', + type: 'keyword', + fieldName: 'clientip', + fieldValue: '50.184.59.162', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'event.dataset:sample_web_logs', + type: 'keyword', + fieldName: 'event.dataset', + fieldValue: 'sample_web_logs', + doc_count: 329, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'extension.keyword:', + type: 'keyword', + fieldName: 'extension.keyword', + fieldValue: '', + doc_count: 196, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'extension.keyword:gz', + type: 'keyword', + fieldName: 'extension.keyword', + fieldValue: 'gz', + doc_count: 42, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'extension.keyword:zip', + type: 'keyword', + fieldName: 'extension.keyword', + fieldValue: 'zip', + doc_count: 28, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'extension.keyword:css', + type: 'keyword', + fieldName: 'extension.keyword', + fieldValue: 'css', + doc_count: 27, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'extension.keyword:deb', + type: 'keyword', + fieldName: 'extension.keyword', + fieldValue: 'deb', + doc_count: 26, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'extension.keyword:rpm', + type: 'keyword', + fieldName: 'extension.keyword', + fieldValue: 'rpm', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:IN', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'IN', + doc_count: 135, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:CN', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'CN', + doc_count: 38, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:US', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'US', + doc_count: 18, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:ID', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'ID', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:BD', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'BD', + doc_count: 7, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:BR', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'BR', + doc_count: 7, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:NG', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'NG', + doc_count: 7, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:AR', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'AR', + doc_count: 4, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:DE', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'DE', + doc_count: 4, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.dest:ET', + type: 'keyword', + fieldName: 'geo.dest', + fieldValue: 'ET', + doc_count: 4, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.src:US', + type: 'keyword', + fieldName: 'geo.src', + fieldValue: 'US', + doc_count: 329, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:IN', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:IN', + doc_count: 135, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:CN', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:CN', + doc_count: 38, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:US', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:US', + doc_count: 18, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:ID', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:ID', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:BD', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:BD', + doc_count: 7, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:BR', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:BR', + doc_count: 7, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:NG', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:NG', + doc_count: 7, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:AR', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:AR', + doc_count: 4, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:DE', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:DE', + doc_count: 4, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'geo.srcdest:US:ET', + type: 'keyword', + fieldName: 'geo.srcdest', + fieldValue: 'US:ET', + doc_count: 4, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'host.keyword:elastic-elastic-elastic.org', + type: 'keyword', + fieldName: 'host.keyword', + fieldValue: 'elastic-elastic-elastic.org', + doc_count: 112, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'host.keyword:artifacts.elastic.co', + type: 'keyword', + fieldName: 'host.keyword', + fieldValue: 'artifacts.elastic.co', + doc_count: 106, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'host.keyword:www.elastic.co', + type: 'keyword', + fieldName: 'host.keyword', + fieldValue: 'www.elastic.co', + doc_count: 84, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'host.keyword:cdn.elastic-elastic-elastic.org', + type: 'keyword', + fieldName: 'host.keyword', + fieldValue: 'cdn.elastic-elastic-elastic.org', + doc_count: 27, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'index.keyword:kibana_sample_data_logs', + type: 'keyword', + fieldName: 'index.keyword', + fieldValue: 'kibana_sample_data_logs', + doc_count: 329, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:30.156.16.163', + type: 'keyword', + fieldName: 'ip', + fieldValue: '30.156.16.163', + doc_count: 101, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:107.152.89.90', + type: 'keyword', + fieldName: 'ip', + fieldValue: '107.152.89.90', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:112.106.69.227', + type: 'keyword', + fieldName: 'ip', + fieldValue: '112.106.69.227', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:160.20.100.193', + type: 'keyword', + fieldName: 'ip', + fieldValue: '160.20.100.193', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:186.153.168.71', + type: 'keyword', + fieldName: 'ip', + fieldValue: '186.153.168.71', + doc_count: 3, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:16.241.165.21', + type: 'keyword', + fieldName: 'ip', + fieldValue: '16.241.165.21', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:20.129.3.8', + type: 'keyword', + fieldName: 'ip', + fieldValue: '20.129.3.8', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:24.42.142.201', + type: 'keyword', + fieldName: 'ip', + fieldValue: '24.42.142.201', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:43.86.71.5', + type: 'keyword', + fieldName: 'ip', + fieldValue: '43.86.71.5', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'ip:50.184.59.162', + type: 'keyword', + fieldName: 'ip', + fieldValue: '50.184.59.162', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'machine.os.keyword:win xp', + type: 'keyword', + fieldName: 'machine.os.keyword', + fieldValue: 'win xp', + doc_count: 148, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'machine.os.keyword:osx', + type: 'keyword', + fieldName: 'machine.os.keyword', + fieldValue: 'osx', + doc_count: 50, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'machine.os.keyword:ios', + type: 'keyword', + fieldName: 'machine.os.keyword', + fieldValue: 'ios', + doc_count: 44, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'machine.os.keyword:win 7', + type: 'keyword', + fieldName: 'machine.os.keyword', + fieldValue: 'win 7', + doc_count: 44, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'machine.os.keyword:win 8', + type: 'keyword', + fieldName: 'machine.os.keyword', + fieldValue: 'win 8', + doc_count: 43, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://www.elastic-elastic-elastic.com/success/timothy-l-kopra', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://www.elastic-elastic-elastic.com/success/timothy-l-kopra', + doc_count: 101, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://facebook.com/success/daniel-barry', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://facebook.com/success/daniel-barry', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://facebook.com/success/mark-kelly', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://facebook.com/success/mark-kelly', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://facebook.com/success/pavel-popovich', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://facebook.com/success/pavel-popovich', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://facebook.com/success/scott-altman', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://facebook.com/success/scott-altman', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://twitter.com/success/dafydd-williams', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://twitter.com/success/dafydd-williams', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://twitter.com/success/valentin-lebedev', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://twitter.com/success/valentin-lebedev', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://twitter.com/success/viktor-m-afanasyev', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://twitter.com/success/viktor-m-afanasyev', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://twitter.com/success/y-ng-l-w-i', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://twitter.com/success/y-ng-l-w-i', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'referer:http://www.elastic-elastic-elastic.com/success/georgi-dobrovolski', + type: 'keyword', + fieldName: 'referer', + fieldValue: 'http://www.elastic-elastic-elastic.com/success/georgi-dobrovolski', + doc_count: 2, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/apm', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/apm', + doc_count: 19, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/beats', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/beats', + doc_count: 13, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/beats/filebeat', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/beats/filebeat', + doc_count: 11, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/elasticsearch/elasticsearch-6.3.2.tar.gz', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/elasticsearch/elasticsearch-6.3.2.tar.gz', + doc_count: 11, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/kibana/kibana-6.3.2-darwin-x86_64.tar.gz', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/kibana/kibana-6.3.2-darwin-x86_64.tar.gz', + doc_count: 11, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/apm-server/apm-server-6.3.2-windows-x86.zip', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/apm-server/apm-server-6.3.2-windows-x86.zip', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/beats/metricbeat', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/beats/metricbeat', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/beats/metricbeat/metricbeat-6.3.2-i686.rpm', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/beats/metricbeat/metricbeat-6.3.2-i686.rpm', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'request.keyword:/elasticsearch/elasticsearch-6.3.2.deb', + type: 'keyword', + fieldName: 'request.keyword', + fieldValue: '/elasticsearch/elasticsearch-6.3.2.deb', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'response.keyword:200', + type: 'keyword', + fieldName: 'response.keyword', + fieldValue: '200', + doc_count: 210, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'response.keyword:404', + type: 'keyword', + fieldName: 'response.keyword', + fieldValue: '404', + doc_count: 110, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'response.keyword:503', + type: 'keyword', + fieldName: 'response.keyword', + fieldValue: '503', + doc_count: 9, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'tags.keyword:success', + type: 'keyword', + fieldName: 'tags.keyword', + fieldValue: 'success', + doc_count: 295, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'tags.keyword:info', + type: 'keyword', + fieldName: 'tags.keyword', + fieldValue: 'info', + doc_count: 279, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'tags.keyword:security', + type: 'keyword', + fieldName: 'tags.keyword', + fieldValue: 'security', + doc_count: 37, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'tags.keyword:warning', + type: 'keyword', + fieldName: 'tags.keyword', + fieldValue: 'warning', + doc_count: 23, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'tags.keyword:login', + type: 'keyword', + fieldName: 'tags.keyword', + fieldValue: 'login', + doc_count: 13, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'tags.keyword:error', + type: 'keyword', + fieldName: 'tags.keyword', + fieldValue: 'error', + doc_count: 11, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://www.elastic.co/downloads/apm', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://www.elastic.co/downloads/apm', + doc_count: 16, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://www.elastic.co/downloads/beats', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://www.elastic.co/downloads/beats', + doc_count: 13, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.tar.gz', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.tar.gz', + doc_count: 11, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://artifacts.elastic.co/downloads/kibana/kibana-6.3.2-darwin-x86_64.tar.gz', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://artifacts.elastic.co/downloads/kibana/kibana-6.3.2-darwin-x86_64.tar.gz', + doc_count: 11, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://www.elastic.co/downloads/beats/filebeat', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://www.elastic.co/downloads/beats/filebeat', + doc_count: 11, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://artifacts.elastic.co/downloads/apm-server/apm-server-6.3.2-windows-x86.zip', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: + 'https://artifacts.elastic.co/downloads/apm-server/apm-server-6.3.2-windows-x86.zip', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-6.3.2-i686.rpm', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-6.3.2-i686.rpm', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.deb', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.deb', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://www.elastic.co/downloads/beats/metricbeat', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://www.elastic.co/downloads/beats/metricbeat', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, + { + key: 'url.keyword:https://www.elastic.co/downloads/enterprise', + type: 'keyword', + fieldName: 'url.keyword', + fieldValue: 'https://www.elastic.co/downloads/enterprise', + doc_count: 10, + bg_count: 0, + total_doc_count: 0, + total_bg_count: 0, + score: 0, + pValue: 1, + normalizedScore: 0, + }, +]; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_terms_search_response.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_terms_search_response.ts new file mode 100644 index 0000000000000..ef2f0f9eb6a26 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/__mocks__/top_terms_search_response.ts @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const topTermsSearchResponseMock = { + took: 8, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 329, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + top_terms_0: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24', + doc_count: 179, + }, + { + key: 'Mozilla/5.0 (X11; Linux x86_64; rv:6.0a1) Gecko/20110421 Firefox/6.0a1', + doc_count: 87, + }, + { + key: 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)', + doc_count: 63, + }, + ], + }, + top_terms_1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 207, + buckets: [ + { key: '30.156.16.164', doc_count: 100 }, + { key: '107.152.89.90', doc_count: 3 }, + { key: '112.106.69.227', doc_count: 3 }, + { key: '160.20.100.193', doc_count: 3 }, + { key: '186.153.168.71', doc_count: 3 }, + { key: '16.241.165.21', doc_count: 2 }, + { key: '20.129.3.8', doc_count: 2 }, + { key: '24.42.142.201', doc_count: 2 }, + { key: '43.86.71.5', doc_count: 2 }, + { key: '50.184.59.162', doc_count: 2 }, + ], + }, + top_terms_2: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'sample_web_logs', doc_count: 329 }], + }, + top_terms_3: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: '', doc_count: 196 }, + { key: 'gz', doc_count: 42 }, + { key: 'zip', doc_count: 28 }, + { key: 'css', doc_count: 27 }, + { key: 'deb', doc_count: 26 }, + { key: 'rpm', doc_count: 10 }, + ], + }, + top_terms_4: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 95, + buckets: [ + { key: 'IN', doc_count: 135 }, + { key: 'CN', doc_count: 38 }, + { key: 'US', doc_count: 18 }, + { key: 'ID', doc_count: 10 }, + { key: 'BD', doc_count: 7 }, + { key: 'BR', doc_count: 7 }, + { key: 'NG', doc_count: 7 }, + { key: 'AR', doc_count: 4 }, + { key: 'DE', doc_count: 4 }, + { key: 'ET', doc_count: 4 }, + ], + }, + top_terms_5: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'US', doc_count: 329 }], + }, + top_terms_6: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 95, + buckets: [ + { key: 'US:IN', doc_count: 135 }, + { key: 'US:CN', doc_count: 38 }, + { key: 'US:US', doc_count: 18 }, + { key: 'US:ID', doc_count: 10 }, + { key: 'US:BD', doc_count: 7 }, + { key: 'US:BR', doc_count: 7 }, + { key: 'US:NG', doc_count: 7 }, + { key: 'US:AR', doc_count: 4 }, + { key: 'US:DE', doc_count: 4 }, + { key: 'US:ET', doc_count: 4 }, + ], + }, + top_terms_7: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'elastic-elastic-elastic.org', doc_count: 112 }, + { key: 'artifacts.elastic.co', doc_count: 106 }, + { key: 'www.elastic.co', doc_count: 84 }, + { key: 'cdn.elastic-elastic-elastic.org', doc_count: 27 }, + ], + }, + top_terms_8: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'kibana_sample_data_logs', doc_count: 329 }], + }, + top_terms_9: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 206, + buckets: [ + { key: '30.156.16.163', doc_count: 101 }, + { key: '107.152.89.90', doc_count: 3 }, + { key: '112.106.69.227', doc_count: 3 }, + { key: '160.20.100.193', doc_count: 3 }, + { key: '186.153.168.71', doc_count: 3 }, + { key: '16.241.165.21', doc_count: 2 }, + { key: '20.129.3.8', doc_count: 2 }, + { key: '24.42.142.201', doc_count: 2 }, + { key: '43.86.71.5', doc_count: 2 }, + { key: '50.184.59.162', doc_count: 2 }, + ], + }, + top_terms_10: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'win xp', doc_count: 148 }, + { key: 'osx', doc_count: 50 }, + { key: 'ios', doc_count: 44 }, + { key: 'win 7', doc_count: 44 }, + { key: 'win 8', doc_count: 43 }, + ], + }, + top_terms_11: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 210, + buckets: [ + { key: 'http://www.elastic-elastic-elastic.com/success/timothy-l-kopra', doc_count: 101 }, + { key: 'http://facebook.com/success/daniel-barry', doc_count: 2 }, + { key: 'http://facebook.com/success/mark-kelly', doc_count: 2 }, + { key: 'http://facebook.com/success/pavel-popovich', doc_count: 2 }, + { key: 'http://facebook.com/success/scott-altman', doc_count: 2 }, + { key: 'http://twitter.com/success/dafydd-williams', doc_count: 2 }, + { key: 'http://twitter.com/success/valentin-lebedev', doc_count: 2 }, + { key: 'http://twitter.com/success/viktor-m-afanasyev', doc_count: 2 }, + { key: 'http://twitter.com/success/y-ng-l-w-i', doc_count: 2 }, + { + key: 'http://www.elastic-elastic-elastic.com/success/georgi-dobrovolski', + doc_count: 2, + }, + ], + }, + top_terms_12: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 214, + buckets: [ + { key: '/apm', doc_count: 19 }, + { key: '/beats', doc_count: 13 }, + { key: '/beats/filebeat', doc_count: 11 }, + { key: '/elasticsearch/elasticsearch-6.3.2.tar.gz', doc_count: 11 }, + { key: '/kibana/kibana-6.3.2-darwin-x86_64.tar.gz', doc_count: 11 }, + { key: '/', doc_count: 10 }, + { key: '/apm-server/apm-server-6.3.2-windows-x86.zip', doc_count: 10 }, + { key: '/beats/metricbeat', doc_count: 10 }, + { key: '/beats/metricbeat/metricbeat-6.3.2-i686.rpm', doc_count: 10 }, + { key: '/elasticsearch/elasticsearch-6.3.2.deb', doc_count: 10 }, + ], + }, + top_terms_13: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: '200', doc_count: 210 }, + { key: '404', doc_count: 110 }, + { key: '503', doc_count: 9 }, + ], + }, + top_terms_14: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'success', doc_count: 295 }, + { key: 'info', doc_count: 279 }, + { key: 'security', doc_count: 37 }, + { key: 'warning', doc_count: 23 }, + { key: 'login', doc_count: 13 }, + { key: 'error', doc_count: 11 }, + ], + }, + top_terms_15: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 217, + buckets: [ + { key: 'https://www.elastic.co/downloads/apm', doc_count: 16 }, + { key: 'https://www.elastic.co/downloads/beats', doc_count: 13 }, + { + key: 'https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.tar.gz', + doc_count: 11, + }, + { + key: 'https://artifacts.elastic.co/downloads/kibana/kibana-6.3.2-darwin-x86_64.tar.gz', + doc_count: 11, + }, + { key: 'https://www.elastic.co/downloads/beats/filebeat', doc_count: 11 }, + { + key: 'https://artifacts.elastic.co/downloads/apm-server/apm-server-6.3.2-windows-x86.zip', + doc_count: 10, + }, + { + key: 'https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-6.3.2-i686.rpm', + doc_count: 10, + }, + { + key: 'https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.2.deb', + doc_count: 10, + }, + { key: 'https://www.elastic.co/downloads/beats/metricbeat', doc_count: 10 }, + { key: 'https://www.elastic.co/downloads/enterprise', doc_count: 10 }, + ], + }, + }, +}; diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.test.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.test.ts index 86896e1c33a01..64477ab64aff9 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.test.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.test.ts @@ -67,7 +67,6 @@ describe('getCategoryRequest', () => { // time range filter whatsoever, for example for start/end (0,50). expect(query).toEqual({ index: 'the-index', - size: 0, body: { query: { bool: { @@ -117,6 +116,7 @@ describe('getCategoryRequest', () => { }, }, }, + size: 0, }, }); }); diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.ts index 0b1c580d93a01..2a06b36a9fac4 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_categories.ts @@ -84,6 +84,7 @@ export const getCategoryRequest = ( wrap, undefined, undefined, + false, false ); diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_categories.test.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_categories.test.ts new file mode 100644 index 0000000000000..e7bdd6c7944d7 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_categories.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 { paramsMock } from './__mocks__/params_match_all'; + +import type { ElasticsearchClient } from '@kbn/core/server'; +import type { Logger } from '@kbn/logging'; + +import { topCategoriesSearchResponseMock } from './__mocks__/top_categories_search_response'; +import { topCategoriesResultMock } from './__mocks__/top_categories_result'; +import { fetchTopCategories } from './fetch_top_categories'; + +const esClientMock = { + msearch: jest.fn().mockImplementation(() => topCategoriesSearchResponseMock), +} as unknown as ElasticsearchClient; + +const loggerMock = {} as unknown as Logger; + +describe('fetchTopCategories', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should fetch top categories successfully', async () => { + const abortSignal = new AbortController().signal; + + const result = await fetchTopCategories({ + esClient: esClientMock, + logger: loggerMock, + emitError: jest.fn(), + abortSignal, + arguments: { ...paramsMock, fieldNames: ['message'] }, + }); + expect(result).toEqual(topCategoriesResultMock); + expect(esClientMock.msearch).toHaveBeenCalledWith( + { + searches: [ + { index: 'the-index' }, + { + aggs: { + categories: { + aggs: { + examples: { + top_hits: { _source: 'message', size: 4, sort: ['the-time-field-name'] }, + }, + }, + categorize_text: { field: 'message', size: 1000 }, + }, + }, + query: { + bool: { + filter: [ + { + bool: { + should: [ + { + range: { + 'the-time-field-name': { + format: 'epoch_millis', + gte: 10, + lte: 20, + }, + }, + }, + { + range: { + 'the-time-field-name': { + format: 'epoch_millis', + gte: 30, + lte: 40, + }, + }, + }, + ], + }, + }, + ], + }, + }, + size: 0, + }, + ], + }, + { maxRetries: 0, signal: abortSignal } + ); + }); +}); diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_categories.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_categories.ts index a9fb020c10c51..2806c21834405 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_categories.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_categories.ts @@ -7,24 +7,21 @@ import { uniq } from 'lodash'; -import type { ElasticsearchClient } from '@kbn/core/server'; -import type { Logger } from '@kbn/logging'; import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils'; -import type { AiopsLogRateAnalysisSchema } from '../api/schema'; - import { fetchCategories } from './fetch_categories'; +import type { FetchTopOptions } from './fetch_top_types'; -export const fetchTopCategories = async ( - esClient: ElasticsearchClient, - params: AiopsLogRateAnalysisSchema, - fieldNames: string[], - logger: Logger, +export const fetchTopCategories = async ({ + esClient, + abortSignal, + emitError, + logger, + arguments: args, +}: FetchTopOptions) => { // The default value of 1 means no sampling will be used - sampleProbability: number = 1, - emitError: (m: string) => void, - abortSignal?: AbortSignal -) => { + const { fieldNames, sampleProbability = 1, ...params } = args; + const categoriesOverall = await fetchCategories( esClient, params, diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_terms.test.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_terms.test.ts new file mode 100644 index 0000000000000..761c39c00e74a --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_terms.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { paramsMock } from './__mocks__/params_match_all'; + +import type { ElasticsearchClient } from '@kbn/core/server'; +import type { Logger } from '@kbn/logging'; + +import { topTermsSearchResponseMock } from './__mocks__/top_terms_search_response'; +import { topTermsResult } from './__mocks__/top_terms_result'; +import { fetchTopTerms } from './fetch_top_terms'; + +const esClientMock = { + search: jest.fn().mockImplementation(() => topTermsSearchResponseMock), +} as unknown as ElasticsearchClient; + +const loggerMock = {} as unknown as Logger; + +describe('fetchTopTerms', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('should fetch top terms successfully', async () => { + const abortSignal = new AbortController().signal; + + const result = await fetchTopTerms({ + esClient: esClientMock, + logger: loggerMock, + emitError: jest.fn(), + abortSignal, + arguments: { + ...paramsMock, + fieldNames: [ + 'agent.keyword', + 'clientip', + 'event.dataset', + 'extension.keyword', + 'geo.dest', + 'geo.src', + 'geo.srcdest', + 'host.keyword', + 'index.keyword', + 'ip', + 'machine.os.keyword', + 'referer', + 'request.keyword', + 'response.keyword', + 'tags.keyword', + 'url.keyword', + ], + }, + }); + + expect(esClientMock.search).toHaveBeenCalledTimes(1); + expect(result).toEqual(topTermsResult); + }); +}); diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_terms.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_terms.ts index 8c5688d542083..ed39c37cbbb97 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_terms.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_terms.ts @@ -5,10 +5,9 @@ * 2.0. */ import { uniqBy } from 'lodash'; + import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { ElasticsearchClient } from '@kbn/core/server'; -import type { Logger } from '@kbn/logging'; import { type SignificantItem, SIGNIFICANT_ITEM_TYPE } from '@kbn/ml-agg-utils'; import { createRandomSamplerWrapper, @@ -21,6 +20,7 @@ import { LOG_RATE_ANALYSIS_SETTINGS, RANDOM_SAMPLER_SEED } from '../constants'; import { getQueryWithParams } from './get_query_with_params'; import { getRequestBase } from './get_request_base'; +import type { FetchTopOptions } from './fetch_top_types'; // TODO Consolidate with duplicate `fetchDurationFieldCandidates` in // `x-pack/plugins/observability_solution/apm/server/routes/correlations/queries/fetch_failed_events_correlation_p_values.ts` @@ -87,16 +87,16 @@ interface Aggs extends estypes.AggregationsLongTermsAggregate { buckets: estypes.AggregationsLongTermsBucket[]; } -export const fetchTopTerms = async ( - esClient: ElasticsearchClient, - params: AiopsLogRateAnalysisSchema, - fieldNames: string[], - logger: Logger, +export const fetchTopTerms = async ({ + esClient, + abortSignal, + emitError, + logger, + arguments: args, +}: FetchTopOptions): Promise<SignificantItem[]> => { // The default value of 1 means no sampling will be used - sampleProbability: number = 1, - emitError: (m: string) => void, - abortSignal?: AbortSignal -): Promise<SignificantItem[]> => { + const { fieldNames, sampleProbability = 1, ...params } = args; + const randomSamplerWrapper = createRandomSamplerWrapper({ probability: sampleProbability, seed: RANDOM_SAMPLER_SEED, diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_types.ts b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_types.ts new file mode 100644 index 0000000000000..26d743caa9347 --- /dev/null +++ b/x-pack/packages/ml/aiops_log_rate_analysis/queries/fetch_top_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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ElasticsearchClient } from '@kbn/core/server'; +import type { Logger } from '@kbn/logging'; + +import type { AiopsLogRateAnalysisSchema } from '../api/schema'; + +export interface FetchTopOptions { + esClient: ElasticsearchClient; + logger: Logger; + emitError: (m: string) => void; + abortSignal?: AbortSignal; + arguments: AiopsLogRateAnalysisSchema & { + fieldNames: string[]; + sampleProbability?: number; + }; +} diff --git a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts index dd8f6aef06141..4d065fb062cb5 100644 --- a/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts +++ b/x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts @@ -58,7 +58,10 @@ export const BUILT_IN_MODEL_TAG = 'prepackaged'; export const ELASTIC_MODEL_TAG = 'elastic'; -export const ELASTIC_MODEL_DEFINITIONS: Record<string, ModelDefinition> = Object.freeze({ +export const ELASTIC_MODEL_DEFINITIONS: Record< + string, + Omit<ModelDefinition, 'supported'> +> = Object.freeze({ [ELSER_ID_V1]: { modelName: 'elser', hidden: true, @@ -156,6 +159,8 @@ export interface ModelDefinition { default?: boolean; /** Indicates if model version is recommended for deployment based on the cluster configuration */ recommended?: boolean; + /** Indicates if model version is supported by the cluster */ + supported: boolean; hidden?: boolean; /** Software license of a model, e.g. MIT */ license?: string; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/top_items_handler.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/top_items_handler.ts index b33682a627ca4..a59b46887b5ff 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/top_items_handler.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/analysis_handlers/top_items_handler.ts @@ -133,15 +133,17 @@ export const topItemsHandlerFactory = let fetchedTopTerms: Awaited<ReturnType<typeof fetchTopTerms>>; try { - fetchedTopTerms = await fetchTopTerms( + fetchedTopTerms = await fetchTopTerms({ esClient, - requestBody, - fieldNames, logger, - stateHandler.sampleProbability(), - responseStream.pushError, - abortSignal - ); + emitError: responseStream.pushError, + abortSignal, + arguments: { + ...requestBody, + fieldNames, + sampleProbability: stateHandler.sampleProbability(), + }, + }); } catch (e) { if (!isRequestAbortedError(e)) { logger.error( @@ -183,15 +185,17 @@ export const topItemsHandlerFactory = } else if (isTextFieldCandidates(payload)) { const { textFieldCandidates: fieldNames } = payload; - const topCategoriesForField = await await fetchTopCategories( + const topCategoriesForField = await fetchTopCategories({ esClient, - requestBody, - fieldNames, logger, - stateHandler.sampleProbability(), - responseStream.pushError, - abortSignal - ); + emitError: responseStream.pushError, + abortSignal, + arguments: { + ...requestBody, + fieldNames, + sampleProbability: stateHandler.sampleProbability(), + }, + }); if (topCategoriesForField.length > 0) { topCategories.push(...topCategoriesForField); diff --git a/x-pack/plugins/alerting/server/rule_type_registry.test.ts b/x-pack/plugins/alerting/server/rule_type_registry.test.ts index 3ee3551a301d5..e678228660e51 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.test.ts @@ -564,6 +564,39 @@ describe('Create Lifecycle', () => { }); }); + test('injects custom cost for certain rule types', () => { + const ruleType: RuleType<never, never, never, never, never, 'default', 'recovered', {}> = { + id: 'siem.indicatorRule', + name: 'Test', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + executor: jest.fn(), + category: 'test', + producer: 'alerts', + ruleTaskTimeout: '20m', + validate: { + params: { validate: (params) => params }, + }, + }; + const registry = new RuleTypeRegistry(ruleTypeRegistryParams); + registry.register(ruleType); + expect(taskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1); + expect(taskManager.registerTaskDefinitions.mock.calls[0][0]).toMatchObject({ + 'alerting:siem.indicatorRule': { + timeout: '20m', + title: 'Test', + cost: 10, + }, + }); + }); + test('shallow clones the given rule type', () => { const ruleType: RuleType<never, never, never, never, never, 'default', 'recovered', {}> = { id: 'test', diff --git a/x-pack/plugins/alerting/server/rule_type_registry.ts b/x-pack/plugins/alerting/server/rule_type_registry.ts index d1ffe59df3b6f..bc7a10d767ff0 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry.ts @@ -14,6 +14,7 @@ import { Logger } from '@kbn/core/server'; import { LicensingPluginSetup } from '@kbn/licensing-plugin/server'; import { RunContext, TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; import { stateSchemaByVersion } from '@kbn/alerting-state-types'; +import { TaskCost } from '@kbn/task-manager-plugin/server/task'; import { TaskRunnerFactory } from './task_runner'; import { RuleType, @@ -40,6 +41,9 @@ import { AlertsService } from './alerts_service/alerts_service'; import { getRuleTypeIdValidLegacyConsumers } from './rule_type_registry_deprecated_consumers'; import { AlertingConfig } from './config'; +const RULE_TYPES_WITH_CUSTOM_COST: Record<string, TaskCost> = { + 'siem.indicatorRule': TaskCost.ExtraLarge, +}; export interface ConstructorOptions { config: AlertingConfig; logger: Logger; @@ -289,6 +293,8 @@ export class RuleTypeRegistry { normalizedRuleType as unknown as UntypedNormalizedRuleType ); + const taskCost: TaskCost | undefined = RULE_TYPES_WITH_CUSTOM_COST[ruleType.id]; + this.taskManager.registerTaskDefinitions({ [`alerting:${ruleType.id}`]: { title: ruleType.name, @@ -310,6 +316,7 @@ export class RuleTypeRegistry { spaceId: schema.string(), consumer: schema.maybe(schema.string()), }), + ...(taskCost ? { cost: taskCost } : {}), }, }); 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 242fc3260b7e2..5324a5a793067 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 @@ -184,31 +184,28 @@ describe('AllCasesListGeneric', () => { useLicenseMock.mockReturnValue({ isAtLeastPlatinum: () => true }); appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getAllByTestId('case-details-link')[0]).toHaveAttribute( - 'href', - '/app/security/cases/test' - ); - expect(screen.getAllByTestId('case-details-link')[0]).toHaveTextContent( - useGetCasesMockState.data.cases[0].title - ); - expect( - screen.getAllByTestId('case-user-profile-avatar-damaged_raccoon')[0] - ).toHaveTextContent('DR'); - expect(screen.getAllByTestId('case-table-column-tags-coke')[0]).toHaveAttribute( - 'title', - useGetCasesMockState.data.cases[0].tags[0] - ); - expect( - screen.getAllByTestId('case-table-column-createdAt')[0].querySelector('.euiToolTipAnchor') - ).toHaveTextContent(removeMsFromDate(useGetCasesMockState.data.cases[0].createdAt)); - expect(screen.getByTestId('case-table-case-count')).toHaveTextContent( - `Showing 10 of ${useGetCasesMockState.data.total} cases` - ); + const caseDetailsLinks = await screen.findAllByTestId('case-details-link'); + + expect(caseDetailsLinks[0]).toHaveAttribute('href', '/app/security/cases/test'); + expect(caseDetailsLinks[0]).toHaveTextContent(useGetCasesMockState.data.cases[0].title); + expect( + (await screen.findAllByTestId('case-user-profile-avatar-damaged_raccoon'))[0] + ).toHaveTextContent('DR'); + expect((await screen.findAllByTestId('case-table-column-tags-coke'))[0]).toHaveAttribute( + 'title', + useGetCasesMockState.data.cases[0].tags[0] + ); + expect( + (await screen.findAllByTestId('case-table-column-createdAt'))[0].querySelector( + '.euiToolTipAnchor' + ) + ).toHaveTextContent(removeMsFromDate(useGetCasesMockState.data.cases[0].createdAt)); + expect(await screen.findByTestId('case-table-case-count')).toHaveTextContent( + `Showing 10 of ${useGetCasesMockState.data.total} cases` + ); - expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); - expect(screen.queryByTestId('all-cases-clear-filters-link-icon')).not.toBeInTheDocument(); - }); + expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); + expect(screen.queryByTestId('all-cases-clear-filters-link-icon')).not.toBeInTheDocument(); }); it("should show a tooltip with the assignee's email when hover over the assignee avatar", async () => { @@ -216,21 +213,17 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); - userEvent.hover(screen.queryAllByTestId('case-user-profile-avatar-damaged_raccoon')[0]); + userEvent.hover((await screen.findAllByTestId('case-user-profile-avatar-damaged_raccoon'))[0]); - await waitFor(() => { - expect(screen.getByText('damaged_raccoon@elastic.co')).toBeInTheDocument(); - }); + expect(await screen.findByText('damaged_raccoon@elastic.co')).toBeInTheDocument(); }); it('should show a tooltip with all tags when hovered', async () => { appMockRenderer.render(<AllCasesList />); - userEvent.hover(screen.queryAllByTestId('case-table-column-tags')[0]); + userEvent.hover((await screen.findAllByTestId('case-table-column-tags'))[0]); - await waitFor(() => { - expect(screen.getByTestId('case-table-column-tags-tooltip')).toBeTruthy(); - }); + expect(await screen.findByTestId('case-table-column-tags-tooltip')).toBeTruthy(); }); it('should render empty fields', async () => { @@ -259,8 +252,10 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); - const checkIt = (columnName: string, key: number) => { - const column = screen.getByTestId('cases-table').querySelectorAll('tbody .euiTableRowCell'); + const checkIt = async (columnName: string, key: number) => { + const column = (await screen.findByTestId('cases-table')).querySelectorAll( + 'tbody .euiTableRowCell' + ); expect(column[key].querySelector('.euiTableRowCell--hideForDesktop')).toHaveTextContent( columnName ); @@ -290,7 +285,7 @@ describe('AllCasesListGeneric', () => { }, }); appMockRenderer.render(<AllCasesList isSelectorView={false} />); - userEvent.click(screen.getByTestId('cases-table-add-case')); + userEvent.click(await screen.findByTestId('cases-table-add-case')); await waitFor(() => { expect(onRowClick).not.toHaveBeenCalled(); }); @@ -299,7 +294,7 @@ describe('AllCasesListGeneric', () => { it('should tableHeaderSortButton AllCasesList', async () => { appMockRenderer.render(<AllCasesList />); - userEvent.click(screen.getAllByTestId('tableHeaderSortButton')[0]); + userEvent.click((await screen.findAllByTestId('tableHeaderSortButton'))[0]); await waitFor(() => { expect(useGetCasesMock).toBeCalledWith( @@ -315,28 +310,26 @@ describe('AllCasesListGeneric', () => { it('renders the columns correctly', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - const casesTable = within(screen.getByTestId('cases-table')); - - expect(casesTable.getByTitle('Name')).toBeInTheDocument(); - expect(casesTable.getByTitle('Category')).toBeInTheDocument(); - expect(casesTable.getByTitle('Created on')).toBeInTheDocument(); - expect(casesTable.getByTitle('Updated on')).toBeInTheDocument(); - expect(casesTable.getByTitle('Status')).toBeInTheDocument(); - expect(casesTable.getByTitle('Severity')).toBeInTheDocument(); - expect(casesTable.getByTitle('Tags')).toBeInTheDocument(); - expect(casesTable.getByTitle('Alerts')).toBeInTheDocument(); - expect(casesTable.getByTitle('Comments')).toBeInTheDocument(); - expect(casesTable.getByTitle('External incident')).toBeInTheDocument(); - expect(casesTable.getByTitle('Actions')).toBeInTheDocument(); + const casesTable = within(await screen.findByTestId('cases-table')); + + expect(await casesTable.findByTitle('Name')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Category')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Created on')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Updated on')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Status')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Severity')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Tags')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Alerts')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Comments')).toBeInTheDocument(); + expect(await casesTable.findByTitle('External incident')).toBeInTheDocument(); + expect(await casesTable.findByTitle('Actions')).toBeInTheDocument(); }); it('should not render table utility bar when isSelectorView=true', async () => { appMockRenderer.render(<AllCasesList isSelectorView={true} />); - await waitFor(() => { - expect(screen.queryByTestId('case-table-selected-case-count')).not.toBeInTheDocument(); - expect(screen.queryByTestId('case-table-bulk-actions')).not.toBeInTheDocument(); - }); + expect(screen.queryByTestId('case-table-selected-case-count')).not.toBeInTheDocument(); + expect(screen.queryByTestId('case-table-bulk-actions')).not.toBeInTheDocument(); }); it('should not render table utility bar when the user does not have permissions to delete', async () => { @@ -346,31 +339,28 @@ describe('AllCasesListGeneric', () => { </TestProviders> ); - await waitFor(() => { - expect(screen.queryByTestId('case-table-selected-case-count')).not.toBeInTheDocument(); - expect(screen.queryByTestId('case-table-bulk-actions')).not.toBeInTheDocument(); - }); + expect(screen.queryByTestId('case-table-selected-case-count')).not.toBeInTheDocument(); + expect(screen.queryByTestId('case-table-bulk-actions')).not.toBeInTheDocument(); }); it('should render metrics when isSelectorView=false', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - await waitFor(() => { - expect(screen.getByTestId('cases-metrics-stats')).toBeInTheDocument(); - }); + + expect(await screen.findByTestId('cases-metrics-stats')).toBeInTheDocument(); }); it('should not render metrics when isSelectorView=true', async () => { appMockRenderer.render(<AllCasesList isSelectorView={true} />); - await waitFor(() => { - expect(screen.queryByTestId('case-table-selected-case-count')).not.toBeInTheDocument(); - expect(screen.queryByTestId('cases-metrics-stats')).not.toBeInTheDocument(); - }); + + expect(screen.queryByTestId('case-table-selected-case-count')).not.toBeInTheDocument(); + expect(screen.queryByTestId('cases-metrics-stats')).not.toBeInTheDocument(); }); it('should call onRowClick with no cases and isSelectorView=true when create case is clicked', async () => { appMockRenderer.render(<AllCasesList isSelectorView={true} onRowClick={onRowClick} />); - userEvent.click(screen.getByTestId('cases-table-add-case-filter-bar')); + userEvent.click(await screen.findByTestId('cases-table-add-case-filter-bar')); const isCreateCase = true; + await waitFor(() => { expect(onRowClick).toHaveBeenCalled(); expect(onRowClick).toBeCalledWith(undefined, isCreateCase); @@ -382,7 +372,7 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList isSelectorView={true} onRowClick={onRowClick} />); - userEvent.click(screen.getByTestId(`cases-table-row-select-${theCase.id}`)); + userEvent.click(await screen.findByTestId(`cases-table-row-select-${theCase.id}`)); await waitFor(() => { expect(onRowClick).toHaveBeenCalledWith(theCase); @@ -392,7 +382,7 @@ describe('AllCasesListGeneric', () => { it('should NOT call onRowClick when clicking a case with modal=true', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - userEvent.click(screen.getByTestId('cases-table-row-1')); + userEvent.click(await screen.findByTestId('cases-table-row-1')); await waitFor(() => { expect(onRowClick).not.toHaveBeenCalled(); @@ -403,7 +393,7 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); // 0 is the status filter button label - userEvent.click(screen.getAllByTitle('Status')[1]); + userEvent.click((await screen.findAllByTitle('Status'))[1]); await waitFor(() => { expect(useGetCasesMock).toHaveBeenLastCalledWith( @@ -420,20 +410,19 @@ describe('AllCasesListGeneric', () => { it('should render Name, Category, CreatedOn and Severity columns when isSelectorView=true', async () => { appMockRenderer.render(<AllCasesList isSelectorView={true} />); - await waitFor(() => { - expect(screen.getByTitle('Name')).toBeInTheDocument(); - expect(screen.getByTitle('Category')).toBeInTheDocument(); - expect(screen.getByTitle('Created on')).toBeInTheDocument(); - // 0 is the severity filter button label - expect(screen.getAllByTitle('Severity')[1]).toBeInTheDocument(); - }); + + expect(await screen.findByTitle('Name')).toBeInTheDocument(); + expect(await screen.findByTitle('Category')).toBeInTheDocument(); + expect(await screen.findByTitle('Created on')).toBeInTheDocument(); + // 0 is the severity filter button label + expect((await screen.findAllByTitle('Severity'))[1]).toBeInTheDocument(); }); it('should sort by severity', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); // 0 is the severity filter button label - userEvent.click(screen.getAllByTitle('Severity')[1]); + userEvent.click((await screen.findAllByTitle('Severity'))[1]); await waitFor(() => { expect(useGetCasesMock).toHaveBeenLastCalledWith( @@ -451,7 +440,7 @@ describe('AllCasesListGeneric', () => { it('should sort by title', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - userEvent.click(screen.getByTitle('Name')); + userEvent.click(await screen.findByTitle('Name')); await waitFor(() => { expect(useGetCasesMock).toHaveBeenLastCalledWith( @@ -469,7 +458,7 @@ describe('AllCasesListGeneric', () => { it('should sort by updatedOn', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - userEvent.click(screen.getByTitle('Updated on')); + userEvent.click(await screen.findByTitle('Updated on')); await waitFor(() => { expect(useGetCasesMock).toHaveBeenLastCalledWith( @@ -487,7 +476,7 @@ describe('AllCasesListGeneric', () => { it('should sort by category', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - userEvent.click(screen.getByTitle('Category')); + userEvent.click(await screen.findByTitle('Category')); await waitFor(() => { expect(useGetCasesMock).toHaveBeenLastCalledWith( @@ -505,9 +494,9 @@ describe('AllCasesListGeneric', () => { it('should filter by category', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - userEvent.click(screen.getByTestId('options-filter-popover-button-category')); + userEvent.click(await screen.findByTestId('options-filter-popover-button-category')); await waitForEuiPopoverOpen(); - userEvent.click(screen.getByTestId('options-filter-popover-item-twix')); + userEvent.click(await screen.findByTestId('options-filter-popover-item-twix')); await waitFor(() => { expect(useGetCasesMock).toHaveBeenLastCalledWith({ @@ -524,17 +513,17 @@ describe('AllCasesListGeneric', () => { it('should show the correct count on stats', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - userEvent.click(screen.getByTestId('options-filter-popover-button-status')); + userEvent.click(await screen.findByTestId('options-filter-popover-button-status')); - await waitFor(() => { - expect(screen.getByTestId('options-filter-popover-item-open')).toHaveTextContent('Open (20)'); - expect(screen.getByTestId('options-filter-popover-item-in-progress')).toHaveTextContent( - 'In progress (40)' - ); - expect(screen.getByTestId('options-filter-popover-item-closed')).toHaveTextContent( - 'Closed (130)' - ); - }); + expect(await screen.findByTestId('options-filter-popover-item-open')).toHaveTextContent( + 'Open (20)' + ); + expect(await screen.findByTestId('options-filter-popover-item-in-progress')).toHaveTextContent( + 'In progress (40)' + ); + expect(await screen.findByTestId('options-filter-popover-item-closed')).toHaveTextContent( + 'Closed (130)' + ); }); it('shows Solution column if there are no set owners', async () => { @@ -544,17 +533,13 @@ describe('AllCasesListGeneric', () => { </TestProviders> ); - await waitFor(() => { - expect(screen.getAllByText('Solution')[0]).toBeInTheDocument(); - }); + expect((await screen.findAllByText('Solution'))[0]).toBeInTheDocument(); }); it('hides Solution column if there is a set owner', async () => { appMockRenderer.render(<AllCasesList isSelectorView={false} />); - await waitFor(() => { - expect(screen.queryByText('Solution')).not.toBeInTheDocument(); - }); + expect(screen.queryByText('Solution')).not.toBeInTheDocument(); }); it('should deselect cases when refreshing', async () => { @@ -568,7 +553,7 @@ describe('AllCasesListGeneric', () => { expect(checkbox).toBeChecked(); } - userEvent.click(screen.getByText('Refresh')); + userEvent.click(await screen.findByText('Refresh')); for (const checkbox of checkboxes) { expect(checkbox).not.toBeChecked(); } @@ -593,9 +578,9 @@ describe('AllCasesListGeneric', () => { expect(checkbox).toBeChecked(); } - userEvent.click(screen.getByTestId('options-filter-popover-button-status')); + userEvent.click(await screen.findByTestId('options-filter-popover-button-status')); await waitForEuiPopoverOpen(); - userEvent.click(screen.getByTestId('options-filter-popover-item-open')); + userEvent.click(await screen.findByTestId('options-filter-popover-item-open')); for (const checkbox of checkboxes) { expect(checkbox).not.toBeChecked(); @@ -611,10 +596,8 @@ describe('AllCasesListGeneric', () => { </TestProviders> ); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeTruthy(); - expect(screen.queryAllByTestId('case-table-column-alertsCount').length).toBe(0); - }); + expect(await screen.findByTestId('cases-table')).toBeInTheDocument(); + expect(screen.queryAllByTestId('case-table-column-alertsCount').length).toBe(0); }); it('should show the alerts column if the alert feature is enabled', async () => { @@ -659,13 +642,13 @@ describe('AllCasesListGeneric', () => { describe('Solutions', () => { it('should hide the solutions filter if the owner is provided', async () => { - const { queryByTestId } = render( + render( <TestProviders owner={[SECURITY_SOLUTION_OWNER]}> <AllCasesList /> </TestProviders> ); - expect(queryByTestId('options-filter-popover-button-owner')).toBeFalsy(); + expect(screen.queryByTestId('options-filter-popover-button-owner')).not.toBeInTheDocument(); }); }); @@ -677,16 +660,13 @@ describe('AllCasesListGeneric', () => { it('Renders bulk action', async () => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeInTheDocument(); - }); + expect(await screen.findByTestId('cases-table')).toBeInTheDocument(); - userEvent.click(screen.getByTestId('checkboxSelectAll')); - expect(screen.getByText('Bulk actions')).toBeInTheDocument(); - userEvent.click(screen.getByText('Bulk actions')); + userEvent.click(await screen.findByTestId('checkboxSelectAll')); + userEvent.click(await screen.findByText('Bulk actions')); - expect(screen.getByTestId('case-bulk-action-status')).toBeInTheDocument(); - expect(screen.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(await screen.findByTestId('case-bulk-action-status')).toBeInTheDocument(); + expect(await screen.findByTestId('cases-bulk-action-delete')).toBeInTheDocument(); }); it.each([[CaseStatuses.open], [CaseStatuses['in-progress']], [CaseStatuses.closed]])( @@ -694,25 +674,21 @@ describe('AllCasesListGeneric', () => { async (status) => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeInTheDocument(); - }); - - userEvent.click(screen.getByTestId('checkboxSelectAll')); + expect(await screen.findByTestId('cases-table')).toBeInTheDocument(); - expect(screen.getByText('Bulk actions')).toBeInTheDocument(); + userEvent.click(await screen.findByTestId('checkboxSelectAll')); - userEvent.click(screen.getByText('Bulk actions')); + userEvent.click(await screen.findByText('Bulk actions')); - userEvent.click(screen.getByTestId('case-bulk-action-status'), undefined, { + userEvent.click(await screen.findByTestId('case-bulk-action-status'), undefined, { skipPointerEventsCheck: true, }); - await waitFor(() => { - expect(screen.getByTestId(`cases-bulk-action-status-${status}`)).toBeInTheDocument(); - }); + expect( + await screen.findByTestId(`cases-bulk-action-status-${status}`) + ).toBeInTheDocument(); - userEvent.click(screen.getByTestId(`cases-bulk-action-status-${status}`)); + userEvent.click(await screen.findByTestId(`cases-bulk-action-status-${status}`)); await waitFor(() => { expect(updateCasesSpy).toBeCalledWith({ @@ -734,25 +710,21 @@ describe('AllCasesListGeneric', () => { ])('Bulk update severity: %s', async (severity) => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeInTheDocument(); - }); + expect(await screen.findByTestId('cases-table')).toBeInTheDocument(); - userEvent.click(screen.getByTestId('checkboxSelectAll')); + userEvent.click(await screen.findByTestId('checkboxSelectAll')); - expect(screen.getByText('Bulk actions')).toBeInTheDocument(); + userEvent.click(await screen.findByText('Bulk actions')); - userEvent.click(screen.getByText('Bulk actions')); - - userEvent.click(screen.getByTestId('case-bulk-action-severity'), undefined, { + userEvent.click(await screen.findByTestId('case-bulk-action-severity'), undefined, { skipPointerEventsCheck: true, }); - await waitFor(() => { - expect(screen.getByTestId(`cases-bulk-action-severity-${severity}`)).toBeInTheDocument(); - }); + expect( + await screen.findByTestId(`cases-bulk-action-severity-${severity}`) + ).toBeInTheDocument(); - userEvent.click(screen.getByTestId(`cases-bulk-action-severity-${severity}`)); + userEvent.click(await screen.findByTestId(`cases-bulk-action-severity-${severity}`)); await waitFor(() => { expect(updateCasesSpy).toBeCalledWith({ @@ -768,23 +740,19 @@ describe('AllCasesListGeneric', () => { it('Bulk delete', async () => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeInTheDocument(); - }); - - userEvent.click(screen.getByTestId('checkboxSelectAll')); + expect(await screen.findByTestId('cases-table')).toBeInTheDocument(); - expect(screen.getByText('Bulk actions')).toBeInTheDocument(); + userEvent.click(await screen.findByTestId('checkboxSelectAll')); - userEvent.click(screen.getByText('Bulk actions')); + userEvent.click(await screen.findByText('Bulk actions')); - userEvent.click(screen.getByTestId('cases-bulk-action-delete'), undefined, { + userEvent.click(await screen.findByTestId('cases-bulk-action-delete'), undefined, { skipPointerEventsCheck: true, }); - expect(screen.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + expect(await screen.findByTestId('confirm-delete-case-modal')).toBeInTheDocument(); - userEvent.click(screen.getByTestId('confirmModalConfirmButton')); + userEvent.click(await screen.findByTestId('confirmModalConfirmButton')); await waitFor(() => { expect(deleteCasesSpy).toHaveBeenCalledWith({ @@ -806,18 +774,15 @@ describe('AllCasesListGeneric', () => { appMockRenderer = createAppMockRenderer({ permissions: readCasesPermissions() }); appMockRenderer.render(<AllCasesList />); - expect(screen.getByTestId('checkboxSelectAll')).toBeDisabled(); + expect(await screen.findByTestId('checkboxSelectAll')).toBeDisabled(); for (const theCase of defaultGetCases.data.cases) { - await waitFor(() => { - expect(screen.getByTestId(`checkboxSelectRow-${theCase.id}`)).toBeDisabled(); - }); + expect(await screen.findByTestId(`checkboxSelectRow-${theCase.id}`)).toBeDisabled(); } }); }); - // FLAKY: https://github.com/elastic/kibana/issues/148095 - describe.skip('Row actions', () => { + describe('Row actions', () => { const statusTests = [ [CaseStatuses.open], [CaseStatuses['in-progress']], @@ -835,11 +800,9 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); for (const theCase of defaultGetCases.data.cases) { - await waitFor(() => { - expect( - screen.getByTestId(`case-action-popover-button-${theCase.id}`) - ).toBeInTheDocument(); - }); + expect( + await screen.findByTestId(`case-action-popover-button-${theCase.id}`) + ).toBeInTheDocument(); } }); @@ -849,21 +812,17 @@ describe('AllCasesListGeneric', () => { const inProgressCase = useGetCasesMockState.data.cases[1]; const theCase = status === CaseStatuses.open ? inProgressCase : openCase; - expect(screen.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeInTheDocument(); - - userEvent.click(screen.getByTestId(`case-action-popover-button-${theCase.id}`)); + userEvent.click(await screen.findByTestId(`case-action-popover-button-${theCase.id}`)); - expect(screen.getByTestId(`case-action-status-panel-${theCase.id}`)).toBeInTheDocument(); - - userEvent.click(screen.getByTestId(`case-action-status-panel-${theCase.id}`), undefined, { - skipPointerEventsCheck: true, - }); - - await waitFor(() => { - expect(screen.getByTestId(`cases-bulk-action-status-${status}`)).toBeInTheDocument(); - }); + userEvent.click( + await screen.findByTestId(`case-action-status-panel-${theCase.id}`), + undefined, + { + skipPointerEventsCheck: true, + } + ); - userEvent.click(screen.getByTestId(`cases-bulk-action-status-${status}`)); + userEvent.click(await screen.findByTestId(`cases-bulk-action-status-${status}`)); await waitFor(() => { expect(updateCasesSpy).toHaveBeenCalledWith({ @@ -878,21 +837,17 @@ describe('AllCasesListGeneric', () => { const mediumCase = useGetCasesMockState.data.cases[1]; const theCase = severity === CaseSeverity.LOW ? mediumCase : lowCase; - expect(screen.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeInTheDocument(); + userEvent.click(await screen.findByTestId(`case-action-popover-button-${theCase.id}`)); - userEvent.click(screen.getByTestId(`case-action-popover-button-${theCase.id}`)); - - expect(screen.getByTestId(`case-action-severity-panel-${theCase.id}`)).toBeInTheDocument(); - - userEvent.click(screen.getByTestId(`case-action-severity-panel-${theCase.id}`), undefined, { - skipPointerEventsCheck: true, - }); - - await waitFor(() => { - expect(screen.getByTestId(`cases-bulk-action-severity-${severity}`)).toBeInTheDocument(); - }); + userEvent.click( + await screen.findByTestId(`case-action-severity-panel-${theCase.id}`), + undefined, + { + skipPointerEventsCheck: true, + } + ); - userEvent.click(screen.getByTestId(`cases-bulk-action-severity-${severity}`)); + userEvent.click(await screen.findByTestId(`cases-bulk-action-severity-${severity}`)); await waitFor(() => { expect(updateCasesSpy).toHaveBeenCalledWith({ @@ -905,19 +860,15 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); const theCase = defaultGetCases.data.cases[0]; - expect(screen.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeInTheDocument(); - - userEvent.click(screen.getByTestId(`case-action-popover-button-${theCase.id}`)); + userEvent.click(await screen.findByTestId(`case-action-popover-button-${theCase.id}`)); - expect(screen.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); - - userEvent.click(screen.getByTestId('cases-bulk-action-delete'), undefined, { + userEvent.click(await screen.findByTestId('cases-bulk-action-delete'), undefined, { skipPointerEventsCheck: true, }); - expect(screen.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); + expect(await screen.findByTestId('confirm-delete-case-modal')).toBeInTheDocument(); - userEvent.click(screen.getByTestId('confirmModalConfirmButton')); + userEvent.click(await screen.findByTestId('confirmModalConfirmButton')); await waitFor(() => { expect(deleteCasesSpy).toHaveBeenCalledWith({ caseIds: ['basic-case-id'] }); @@ -927,12 +878,12 @@ describe('AllCasesListGeneric', () => { it('should disable row actions when bulk selecting all cases', async () => { appMockRenderer.render(<AllCasesList />); - userEvent.click(screen.getByTestId('checkboxSelectAll')); + userEvent.click(await screen.findByTestId('checkboxSelectAll')); for (const theCase of defaultGetCases.data.cases) { - await waitFor(() => { - expect(screen.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeDisabled(); - }); + expect( + await screen.findByTestId(`case-action-popover-button-${theCase.id}`) + ).toBeDisabled(); } }); @@ -940,12 +891,12 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); const caseToSelect = defaultGetCases.data.cases[0]; - userEvent.click(screen.getByTestId(`checkboxSelectRow-${caseToSelect.id}`)); + userEvent.click(await screen.findByTestId(`checkboxSelectRow-${caseToSelect.id}`)); for (const theCase of defaultGetCases.data.cases) { - await waitFor(() => { - expect(screen.getByTestId(`case-action-popover-button-${theCase.id}`)).toBeDisabled(); - }); + expect( + await screen.findByTestId(`case-action-popover-button-${theCase.id}`) + ).toBeDisabled(); } }); }); @@ -956,10 +907,8 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeTruthy(); - expect(screen.queryAllByTestId('case-table-column-assignee').length).toBe(0); - }); + expect(await screen.findByTestId('cases-table')).toBeTruthy(); + expect(screen.queryAllByTestId('case-table-column-assignee').length).toBe(0); }); it('should show the assignees column on platinum license', async () => { @@ -967,10 +916,8 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeTruthy(); - expect(screen.queryAllByTestId('case-table-column-assignee').length).toBeGreaterThan(0); - }); + expect(await screen.findByTestId('cases-table')).toBeTruthy(); + expect(screen.queryAllByTestId('case-table-column-assignee').length).toBeGreaterThan(0); }); it('should hide the assignees filters on basic license', async () => { @@ -978,10 +925,8 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeTruthy(); - expect(screen.queryAllByTestId('options-filter-popover-button-assignees').length).toBe(0); - }); + expect(await screen.findByTestId('cases-table')).toBeTruthy(); + expect(screen.queryAllByTestId('options-filter-popover-button-assignees').length).toBe(0); }); it('should show the assignees filters on platinum license', async () => { @@ -989,12 +934,10 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); - await waitFor(() => { - expect(screen.getByTestId('cases-table')).toBeTruthy(); - expect( - screen.queryAllByTestId('options-filter-popover-button-assignees').length - ).toBeGreaterThan(0); - }); + expect(await screen.findByTestId('cases-table')).toBeTruthy(); + expect( + screen.queryAllByTestId('options-filter-popover-button-assignees').length + ).toBeGreaterThan(0); }); it('should reset the assignees when deactivating the filter', async () => { @@ -1003,15 +946,19 @@ describe('AllCasesListGeneric', () => { appMockRenderer.render(<AllCasesList />); // Opens assignees filter and checks an option - const assigneesButton = screen.getByTestId('options-filter-popover-button-assignees'); + const assigneesButton = await screen.findByTestId( + 'options-filter-popover-button-assignees' + ); userEvent.click(assigneesButton); - userEvent.click(screen.getByText('Damaged Raccoon')); - expect(within(assigneesButton).getByLabelText('1 active filters')).toBeInTheDocument(); + userEvent.click(await screen.findByText('Damaged Raccoon')); + expect( + await within(assigneesButton).findByLabelText('1 active filters') + ).toBeInTheDocument(); // Deactivates assignees filter - userEvent.click(screen.getByRole('button', { name: 'More' })); + userEvent.click(await screen.findByRole('button', { name: 'More' })); await waitForEuiPopoverOpen(); - userEvent.click(screen.getByRole('option', { name: 'Assignees' })); + userEvent.click(await screen.findByRole('option', { name: 'Assignees' })); expect(useGetCasesMock).toHaveBeenLastCalledWith({ filterOptions: { @@ -1022,14 +969,14 @@ describe('AllCasesListGeneric', () => { }); // Reopens assignees filter - userEvent.click(screen.getByRole('option', { name: 'Assignees' })); + userEvent.click(await screen.findByRole('option', { name: 'Assignees' })); // Opens the assignees popup userEvent.click(assigneesButton); - expect(screen.getByLabelText('click to filter assignees')).toBeInTheDocument(); + expect(await screen.findByLabelText('click to filter assignees')).toBeInTheDocument(); expect( - within(screen.getByTestId('options-filter-popover-button-assignees')).queryByLabelText( - '1 active filters' - ) + within( + await screen.findByTestId('options-filter-popover-button-assignees') + ).queryByLabelText('1 active filters') ).not.toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.test.tsx b/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.test.tsx index 10bdd185ef9f1..c2575b146b9f8 100644 --- a/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.test.tsx @@ -6,12 +6,11 @@ */ import React from 'react'; import { MultiSelectFilter } from './multi_select_filter'; -import { render, screen } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; -// FLAKY: https://github.com/elastic/kibana/issues/183663 -describe.skip('multi select filter', () => { +describe('multi select filter', () => { it('should render the amount of options available', async () => { const onChange = jest.fn(); const props = { @@ -29,10 +28,10 @@ describe.skip('multi select filter', () => { render(<MultiSelectFilter {...props} />); - userEvent.click(screen.getByRole('button', { name: 'Tags' })); + userEvent.click(await screen.findByRole('button', { name: 'Tags' })); await waitForEuiPopoverOpen(); - expect(screen.getByText('4 options')).toBeInTheDocument(); + expect(await screen.findByText('4 options')).toBeInTheDocument(); }); it('hides the limit reached warning when a selected tag is removed', async () => { @@ -53,14 +52,17 @@ describe.skip('multi select filter', () => { const { rerender } = render(<MultiSelectFilter {...props} />); - userEvent.click(screen.getByRole('button', { name: 'Tags' })); + userEvent.click(await screen.findByRole('button', { name: 'Tags' })); await waitForEuiPopoverOpen(); - expect(screen.getByText('Limit reached')).toBeInTheDocument(); + expect(await screen.findByText('Limit reached')).toBeInTheDocument(); - userEvent.click(screen.getByRole('option', { name: 'tag a' })); + userEvent.click(await screen.findByRole('option', { name: 'tag a' })); + + await waitFor(() => { + expect(onChange).toHaveBeenCalledWith({ filterId: 'tags', selectedOptionKeys: [] }); + }); - expect(onChange).toHaveBeenCalledWith({ filterId: 'tags', selectedOptionKeys: [] }); rerender(<MultiSelectFilter {...props} selectedOptionKeys={[]} />); expect(screen.queryByText('Limit reached')).not.toBeInTheDocument(); @@ -84,20 +86,23 @@ describe.skip('multi select filter', () => { const { rerender } = render(<MultiSelectFilter {...props} />); - userEvent.click(screen.getByRole('button', { name: 'Tags' })); + userEvent.click(await screen.findByRole('button', { name: 'Tags' })); await waitForEuiPopoverOpen(); expect(screen.queryByText('Limit reached')).not.toBeInTheDocument(); - userEvent.click(screen.getByRole('option', { name: 'tag b' })); + userEvent.click(await screen.findByRole('option', { name: 'tag b' })); - expect(onChange).toHaveBeenCalledWith({ - filterId: 'tags', - selectedOptionKeys: ['tag a', 'tag b'], + await waitFor(() => { + expect(onChange).toHaveBeenCalledWith({ + filterId: 'tags', + selectedOptionKeys: ['tag a', 'tag b'], + }); }); + rerender(<MultiSelectFilter {...props} selectedOptionKeys={['tag a', 'tag b']} />); - expect(screen.getByText('Limit reached')).toBeInTheDocument(); + expect(await screen.findByText('Limit reached')).toBeInTheDocument(); }); it('should not call onChange when the limit has been reached', async () => { @@ -118,14 +123,16 @@ describe.skip('multi select filter', () => { render(<MultiSelectFilter {...props} />); - userEvent.click(screen.getByRole('button', { name: 'Tags' })); + userEvent.click(await screen.findByRole('button', { name: 'Tags' })); await waitForEuiPopoverOpen(); - expect(screen.getByText('Limit reached')).toBeInTheDocument(); + expect(await screen.findByText('Limit reached')).toBeInTheDocument(); - userEvent.click(screen.getByRole('option', { name: 'tag b' })); + userEvent.click(await screen.findByRole('option', { name: 'tag b' })); - expect(onChange).not.toHaveBeenCalled(); + await waitFor(() => { + expect(onChange).not.toHaveBeenCalled(); + }); }); it('should remove selected option if it suddenly disappeared from the list', async () => { @@ -144,7 +151,10 @@ describe.skip('multi select filter', () => { const { rerender } = render(<MultiSelectFilter {...props} />); rerender(<MultiSelectFilter {...props} options={[{ key: 'tag a', label: 'tag a' }]} />); - expect(onChange).toHaveBeenCalledWith({ filterId: 'tags', selectedOptionKeys: [] }); + + await waitFor(() => { + expect(onChange).toHaveBeenCalledWith({ filterId: 'tags', selectedOptionKeys: [] }); + }); }); it('activates custom renderOption when set', async () => { @@ -164,12 +174,12 @@ describe.skip('multi select filter', () => { }; render(<MultiSelectFilter {...props} />); - userEvent.click(screen.getByRole('button', { name: 'Tags' })); + userEvent.click(await screen.findByRole('button', { name: 'Tags' })); await waitForEuiPopoverOpen(); - expect(screen.getAllByTestId(TEST_ID).length).toBe(2); + expect((await screen.findAllByTestId(TEST_ID)).length).toBe(2); }); - it('should not show the amount of options if hideActiveOptionsNumber is active', () => { + it('should not show the amount of options if hideActiveOptionsNumber is active', async () => { const onChange = jest.fn(); const props = { id: 'tags', @@ -184,7 +194,7 @@ describe.skip('multi select filter', () => { }; const { rerender } = render(<MultiSelectFilter {...props} />); - expect(screen.queryByLabelText('1 active filters')).toBeInTheDocument(); + expect(await screen.findByLabelText('1 active filters')).toBeInTheDocument(); rerender(<MultiSelectFilter {...props} hideActiveOptionsNumber />); expect(screen.queryByLabelText('1 active filters')).not.toBeInTheDocument(); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/status_filter.test.tsx b/x-pack/plugins/cases/public/components/all_cases/status_filter.test.tsx index d1bb46c7b8717..22ac90472a6dc 100644 --- a/x-pack/plugins/cases/public/components/all_cases/status_filter.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/status_filter.test.tsx @@ -19,8 +19,7 @@ const LABELS = { inProgress: i18n.STATUS_IN_PROGRESS, }; -// FLAKY: https://github.com/elastic/kibana/issues/177334 -describe.skip('StatusFilter', () => { +describe('StatusFilter', () => { const onChange = jest.fn(); const defaultProps = { selectedOptionKeys: [], @@ -37,16 +36,18 @@ describe.skip('StatusFilter', () => { it('should render', async () => { render(<StatusFilter {...defaultProps} />); - expect(await screen.findByTestId('options-filter-popover-button-status')).toBeInTheDocument(); expect(await screen.findByTestId('options-filter-popover-button-status')).not.toBeDisabled(); userEvent.click(await screen.findByRole('button', { name: 'Status' })); + await waitForEuiPopoverOpen(); - expect(await screen.findByRole('option', { name: LABELS.open })).toBeInTheDocument(); - expect(await screen.findByRole('option', { name: LABELS.inProgress })).toBeInTheDocument(); - expect(await screen.findByRole('option', { name: LABELS.closed })).toBeInTheDocument(); - expect((await screen.findAllByRole('option')).length).toBe(3); + const options = await screen.findAllByRole('option'); + + expect(options.length).toBe(3); + expect(options[0]).toHaveTextContent(LABELS.open); + expect(options[1]).toHaveTextContent(LABELS.inProgress); + expect(options[2]).toHaveTextContent(LABELS.closed); }); it('should call onStatusChanged when changing status to open', async () => { @@ -68,10 +69,13 @@ describe.skip('StatusFilter', () => { render(<StatusFilter {...defaultProps} hiddenStatuses={[CaseStatuses.closed]} />); userEvent.click(await screen.findByRole('button', { name: 'Status' })); + await waitForEuiPopoverOpen(); - expect(await screen.findAllByRole('option')).toHaveLength(2); - expect(await screen.findByRole('option', { name: LABELS.open })).toBeInTheDocument(); - expect(await screen.findByRole('option', { name: LABELS.inProgress })).toBeInTheDocument(); + const options = await screen.findAllByRole('option'); + + expect(options.length).toBe(2); + expect(options[0]).toHaveTextContent(LABELS.open); + expect(options[1]).toHaveTextContent(LABELS.inProgress); }); }); diff --git a/x-pack/plugins/cases/public/components/case_form_fields/title.test.tsx b/x-pack/plugins/cases/public/components/case_form_fields/title.test.tsx index 3caf90433f8fb..aa1ad06067616 100644 --- a/x-pack/plugins/cases/public/components/case_form_fields/title.test.tsx +++ b/x-pack/plugins/cases/public/components/case_form_fields/title.test.tsx @@ -7,18 +7,21 @@ import type { FC, PropsWithChildren } from 'react'; import React from 'react'; -import { mount } from 'enzyme'; -import { act } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import type { FormHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import type { CaseFormFieldsSchemaProps } from './schema'; + import { useForm, Form } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import userEvent from '@testing-library/user-event'; + import { Title } from './title'; import { schema } from '../create/schema'; -import type { CaseFormFieldsSchemaProps } from './schema'; +import { createAppMockRenderer, type AppMockRenderer } from '../../common/mock'; -// FLAKY: https://github.com/elastic/kibana/issues/187364 -describe.skip('Title', () => { +describe('Title', () => { let globalForm: FormHook; + let appMockRender: AppMockRenderer; const MockHookWrapperComponent: FC<PropsWithChildren<unknown>> = ({ children }) => { const { form } = useForm<CaseFormFieldsSchemaProps>({ @@ -35,42 +38,37 @@ describe.skip('Title', () => { beforeEach(() => { jest.resetAllMocks(); + appMockRender = createAppMockRenderer(); }); it('it renders', async () => { - const wrapper = mount( + appMockRender.render( <MockHookWrapperComponent> <Title isLoading={false} /> </MockHookWrapperComponent> ); - expect(wrapper.find(`[data-test-subj="caseTitle"]`).exists()).toBeTruthy(); + expect(await screen.findByTestId('caseTitle')).toBeInTheDocument(); }); it('it disables the input when loading', async () => { - const wrapper = mount( + appMockRender.render( <MockHookWrapperComponent> <Title isLoading={true} /> </MockHookWrapperComponent> ); - - expect(wrapper.find(`[data-test-subj="caseTitle"] input`).prop('disabled')).toBeTruthy(); + expect(await screen.findByTestId('input')).toBeDisabled(); }); it('it changes the title', async () => { - const wrapper = mount( + appMockRender.render( <MockHookWrapperComponent> <Title isLoading={false} /> </MockHookWrapperComponent> ); - await act(async () => { - wrapper - .find(`[data-test-subj="caseTitle"] input`) - .first() - .simulate('change', { target: { value: 'My new title' } }); - }); + userEvent.paste(await screen.findByTestId('input'), ' is updated'); - expect(globalForm.getFormData()).toEqual({ title: 'My new title' }); + expect(globalForm.getFormData()).toEqual({ title: 'My title is updated' }); }); }); diff --git a/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx index 4da76d846dd9d..2a09c0b207be1 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx +++ b/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; -import { screen } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; @@ -17,8 +17,7 @@ import { MAX_CUSTOM_FIELDS_PER_CASE } from '../../../common/constants'; import { CustomFields } from '.'; import * as i18n from './translations'; -// FLAKY: https://github.com/elastic/kibana/issues/176805 -describe.skip('CustomFields', () => { +describe('CustomFields', () => { let appMockRender: AppMockRenderer; const props = { @@ -68,7 +67,9 @@ describe.skip('CustomFields', () => { userEvent.click(await screen.findByTestId('add-custom-field')); - expect(props.handleAddCustomField).toBeCalled(); + await waitFor(() => { + expect(props.handleAddCustomField).toBeCalled(); + }); }); it('calls handleEditCustomField on edit option click', async () => { @@ -80,13 +81,9 @@ describe.skip('CustomFields', () => { await screen.findByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-edit`) ); - expect(props.handleEditCustomField).toBeCalledWith(customFieldsConfigurationMock[0].key); - }); - - it('shows the experimental badge', async () => { - appMockRender.render(<CustomFields {...props} />); - - expect(await screen.findByTestId('case-experimental-badge')).toBeInTheDocument(); + await waitFor(() => { + expect(props.handleEditCustomField).toBeCalledWith(customFieldsConfigurationMock[0].key); + }); }); it('shows error when custom fields reaches the limit', async () => { diff --git a/x-pack/plugins/cases/public/components/edit_connector/push_button.test.tsx b/x-pack/plugins/cases/public/components/edit_connector/push_button.test.tsx index fee6fdc8d1557..54fae78d22813 100644 --- a/x-pack/plugins/cases/public/components/edit_connector/push_button.test.tsx +++ b/x-pack/plugins/cases/public/components/edit_connector/push_button.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { screen } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import type { AppMockRenderer } from '../../common/mock'; @@ -36,39 +36,42 @@ describe('PushButton ', () => { it('renders the button without tooltip', async () => { appMockRender.render(<PushButton {...defaultProps} />); - expect(screen.getByTestId('push-to-external-service')).toBeInTheDocument(); + expect(await screen.findByTestId('push-to-external-service')).toBeInTheDocument(); expect(screen.queryByTestId('push-button-tooltip')).not.toBeInTheDocument(); }); it('renders the correct label when the connector has not been pushed', async () => { appMockRender.render(<PushButton {...defaultProps} />); - expect(screen.getByText('Push as My SN connector incident')).toBeInTheDocument(); + expect(await screen.findByText('Push as My SN connector incident')).toBeInTheDocument(); }); it('renders the correct label when the connector has been pushed', async () => { appMockRender.render(<PushButton {...defaultProps} hasBeenPushed={true} />); - expect(screen.getByText('Update My SN connector incident')).toBeInTheDocument(); + expect(await screen.findByText('Update My SN connector incident')).toBeInTheDocument(); }); it('pushed correctly', async () => { appMockRender.render(<PushButton {...defaultProps} />); - userEvent.click(screen.getByTestId('push-to-external-service')); - expect(pushToService).toHaveBeenCalled(); + userEvent.click(await screen.findByTestId('push-to-external-service')); + + await waitFor(() => { + expect(pushToService).toHaveBeenCalled(); + }); }); it('disables the button', async () => { appMockRender.render(<PushButton {...defaultProps} disabled={true} />); - expect(screen.getByTestId('push-to-external-service')).toBeDisabled(); + expect(await screen.findByTestId('push-to-external-service')).toBeDisabled(); }); it('shows the tooltip context correctly', async () => { appMockRender.render(<PushButton {...defaultProps} showTooltip={true} />); - userEvent.hover(screen.getByTestId('push-to-external-service')); + userEvent.hover(await screen.findByTestId('push-to-external-service')); expect(await screen.findByText('My SN connector incident is up to date')).toBeInTheDocument(); expect(await screen.findByText('No update is required')).toBeInTheDocument(); @@ -83,7 +86,7 @@ describe('PushButton ', () => { /> ); - userEvent.hover(screen.getByTestId('push-to-external-service')); + userEvent.hover(await screen.findByTestId('push-to-external-service')); expect(await screen.findByText('My title')).toBeInTheDocument(); expect(await screen.findByText('My desc')).toBeInTheDocument(); diff --git a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx index e43eb7a253026..02cfa92f9e2a0 100644 --- a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx +++ b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx @@ -9,18 +9,25 @@ import React from 'react'; import type { ActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; import { fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { showEuiComboBoxOptions } from '@elastic/eui/lib/test/rtl'; +import { useAlertsDataView } from '@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'; import { useApplication } from '../../../common/lib/kibana/use_application'; -import { useAlertDataViews } from '../hooks/use_alert_data_view'; import { CasesParamsFields } from './cases_params'; -import { showEuiComboBoxOptions } from '@elastic/eui/lib/test/rtl'; +import { useKibana } from '../../../common/lib/kibana/kibana_react'; +import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; -jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); +jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'); jest.mock('../../../common/lib/kibana/use_application'); -jest.mock('../hooks/use_alert_data_view'); +jest.mock('../../../common/lib/kibana/kibana_react'); -const useAlertDataViewsMock = useAlertDataViews as jest.Mock; +const useKibanaMock = jest.mocked(useKibana); +const useAlertsDataViewMock = jest.mocked(useAlertsDataView); const useApplicationMock = useApplication as jest.Mock; +useKibanaMock.mockReturnValue({ + services: { ...createStartServicesMock(), data: { dataViews: {} } }, +} as unknown as ReturnType<typeof useKibana>); + const actionParams = { subAction: 'run', subActionParams: { @@ -52,24 +59,25 @@ describe('CasesParamsFields renders', () => { beforeEach(() => { jest.clearAllMocks(); useApplicationMock.mockReturnValueOnce({ appId: 'management' }); - useAlertDataViewsMock.mockReturnValue({ - loading: false, - dataViews: [ - { - title: '.alerts-test', - fields: [ - { - name: 'host.ip', - type: 'ip', - aggregatable: true, - }, - { - name: 'host.geo.location', - type: 'geo_point', - }, - ], - }, - ], + useAlertsDataViewMock.mockReturnValue({ + isLoading: false, + dataView: { + title: '.alerts-test', + fields: [ + { + name: 'host.ip', + type: 'ip', + aggregatable: true, + searchable: true, + }, + { + name: 'host.geo.location', + type: 'geo_point', + aggregatable: false, + searchable: true, + }, + ], + }, }); }); @@ -83,14 +91,14 @@ describe('CasesParamsFields renders', () => { }); it('renders loading state of grouping by fields correctly', async () => { - useAlertDataViewsMock.mockReturnValue({ loading: true }); + useAlertsDataViewMock.mockReturnValue({ isLoading: true }); render(<CasesParamsFields {...defaultProps} />); expect(await screen.findByRole('progressbar')).toBeInTheDocument(); }); it('disables dropdown when loading grouping by fields', async () => { - useAlertDataViewsMock.mockReturnValue({ loading: true }); + useAlertsDataViewMock.mockReturnValue({ isLoading: true }); render(<CasesParamsFields {...defaultProps} />); expect(await screen.findByRole('progressbar')).toBeInTheDocument(); @@ -161,29 +169,31 @@ describe('CasesParamsFields renders', () => { }); it('updates grouping by field by search', async () => { - useAlertDataViewsMock.mockReturnValue({ - loading: false, - dataViews: [ - { - title: '.alerts-test', - fields: [ - { - name: 'host.ip', - type: 'ip', - aggregatable: true, - }, - { - name: 'host.geo.location', - type: 'geo_point', - }, - { - name: 'alert.name', - type: 'string', - aggregatable: true, - }, - ], - }, - ], + useAlertsDataViewMock.mockReturnValue({ + isLoading: false, + dataView: { + title: '.alerts-test', + fields: [ + { + name: 'host.ip', + type: 'ip', + aggregatable: true, + searchable: true, + }, + { + name: 'host.geo.location', + type: 'geo_point', + aggregatable: false, + searchable: true, + }, + { + name: 'alert.name', + type: 'string', + aggregatable: true, + searchable: true, + }, + ], + }, }); render(<CasesParamsFields {...defaultProps} />); diff --git a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx index 71e9380246278..4b667d7880758 100644 --- a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx +++ b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx @@ -8,6 +8,7 @@ import React, { memo, useCallback, useEffect, useMemo } from 'react'; import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public/types'; +import type { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; import type { EuiComboBoxOptionOption } from '@elastic/eui'; import { EuiCheckbox, @@ -19,20 +20,30 @@ import { EuiSpacer, EuiComboBox, } from '@elastic/eui'; -import type { ValidFeatureId } from '@kbn/rule-data-utils'; -import { CASES_CONNECTOR_SUB_ACTION } from '../../../../common/constants'; +import { useAlertsDataView } from '@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'; import * as i18n from './translations'; import type { CasesActionParams } from './types'; +import { CASES_CONNECTOR_SUB_ACTION } from '../../../../common/constants'; import { DEFAULT_TIME_WINDOW, TIME_UNITS } from './constants'; import { getTimeUnitOptions } from './utils'; -import { useAlertDataViews } from '../hooks/use_alert_data_view'; +import { useKibana } from '../../../common/lib/kibana'; export const CasesParamsFieldsComponent: React.FunctionComponent< ActionParamsProps<CasesActionParams> > = ({ actionParams, editAction, errors, index, producerId }) => { - const { dataViews, loading: loadingAlertDataViews } = useAlertDataViews( - producerId ? [producerId as ValidFeatureId] : [] - ); + const { + http, + notifications: { toasts }, + data: { dataViews: dataViewsService }, + } = useKibana().services; + const { dataView, isLoading: loadingAlertDataViews } = useAlertsDataView({ + http, + toasts, + dataViewsService, + featureIds: producerId + ? [producerId as Exclude<ValidFeatureId, typeof AlertConsumers.SIEM>] + : [], + }); const { timeWindow, reopenClosedCases, groupingBy } = useMemo( () => @@ -108,21 +119,17 @@ export const CasesParamsFieldsComponent: React.FunctionComponent< ); const options: Array<EuiComboBoxOptionOption<string>> = useMemo(() => { - if (!dataViews?.length) { + if (!dataView) { return []; } - return dataViews - .map((dataView) => { - return dataView.fields - .filter((field) => Boolean(field.aggregatable)) - .map((field) => ({ - value: field.name, - label: field.name, - })); - }) - .flat(); - }, [dataViews]); + return dataView.fields + .filter((field) => Boolean(field.aggregatable)) + .map((field) => ({ + value: field.name, + label: field.name, + })); + }, [dataView]); const selectedOptions = groupingBy.map((field) => ({ value: field, label: field })); diff --git a/x-pack/plugins/cases/public/components/system_actions/hooks/alert_fields.ts b/x-pack/plugins/cases/public/components/system_actions/hooks/alert_fields.ts deleted file mode 100644 index 8a73674a5e264..0000000000000 --- a/x-pack/plugins/cases/public/components/system_actions/hooks/alert_fields.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { ValidFeatureId } from '@kbn/rule-data-utils'; -import type { HttpSetup } from '@kbn/core/public'; -import type { FieldSpec } from '@kbn/data-views-plugin/common'; -import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; - -export async function fetchAlertFields({ - http, - featureIds, -}: { - http: HttpSetup; - featureIds: ValidFeatureId[]; -}): Promise<FieldSpec[]> { - const { fields: alertFields = [] } = await http.get<{ fields: FieldSpec[] }>( - `${BASE_RAC_ALERTS_API_PATH}/browser_fields`, - { - query: { featureIds }, - } - ); - return alertFields; -} diff --git a/x-pack/plugins/cases/public/components/system_actions/hooks/alert_index.ts b/x-pack/plugins/cases/public/components/system_actions/hooks/alert_index.ts deleted file mode 100644 index 4b5a496f226bb..0000000000000 --- a/x-pack/plugins/cases/public/components/system_actions/hooks/alert_index.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 { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; -import type { HttpSetup } from '@kbn/core/public'; - -export async function fetchAlertIndexNames({ - http, - features, -}: { - http: HttpSetup; - features: string; -}): Promise<string[]> { - const { index_name: indexNamesStr = [] } = await http.get<{ index_name: string[] }>( - `${BASE_RAC_ALERTS_API_PATH}/index`, - { - query: { features }, - } - ); - return indexNamesStr; -} diff --git a/x-pack/plugins/cases/public/components/system_actions/hooks/use_alert_data_view.test.tsx b/x-pack/plugins/cases/public/components/system_actions/hooks/use_alert_data_view.test.tsx deleted file mode 100644 index 8dbb501e4cf15..0000000000000 --- a/x-pack/plugins/cases/public/components/system_actions/hooks/use_alert_data_view.test.tsx +++ /dev/null @@ -1,135 +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 { waitFor } from '@testing-library/react'; -import { renderHook } from '@testing-library/react-hooks/dom'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { AlertConsumers } from '@kbn/rule-data-utils'; -import type { ValidFeatureId } from '@kbn/rule-data-utils'; -import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; -import { useAlertDataViews } from './use_alert_data_view'; - -const mockUseKibanaReturnValue = createStartServicesMock(); - -jest.mock('@kbn/kibana-react-plugin/public', () => ({ - __esModule: true, - useKibana: jest.fn(() => ({ - services: mockUseKibanaReturnValue, - })), -})); - -jest.mock('./alert_index', () => ({ - fetchAlertIndexNames: jest.fn(), -})); - -const { fetchAlertIndexNames } = jest.requireMock('./alert_index'); - -jest.mock('./alert_fields', () => ({ - fetchAlertFields: jest.fn(), -})); -const { fetchAlertFields } = jest.requireMock('./alert_fields'); - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: false, - cacheTime: 0, - }, - }, -}); -const wrapper = ({ children }: { children: Node }) => ( - <QueryClientProvider client={queryClient}> {children} </QueryClientProvider> -); - -describe('useAlertDataView', () => { - const observabilityAlertFeatureIds: ValidFeatureId[] = [ - AlertConsumers.APM, - AlertConsumers.INFRASTRUCTURE, - AlertConsumers.LOGS, - AlertConsumers.UPTIME, - ]; - - beforeEach(() => { - fetchAlertIndexNames.mockResolvedValue([ - '.alerts-observability.uptime.alerts-*', - '.alerts-observability.metrics.alerts-*', - '.alerts-observability.logs.alerts-*', - '.alerts-observability.apm.alerts-*', - ]); - fetchAlertFields.mockResolvedValue([{ data: ' fields' }]); - }); - - afterEach(() => { - queryClient.clear(); - jest.clearAllMocks(); - }); - - it('initially is loading and does not have data', async () => { - const mockedAsyncDataView = { - loading: true, - dataview: undefined, - }; - - const { result } = renderHook(() => useAlertDataViews(observabilityAlertFeatureIds), { - wrapper, - }); - - await waitFor(() => expect(result.current).toEqual(mockedAsyncDataView)); - }); - - it('fetch index names + fields for the provided o11y featureIds', async () => { - renderHook(() => useAlertDataViews(observabilityAlertFeatureIds), { - wrapper, - }); - - await waitFor(() => expect(fetchAlertIndexNames).toHaveBeenCalledTimes(1)); - expect(fetchAlertFields).toHaveBeenCalledTimes(1); - }); - - it('only fetch index names for security featureId', async () => { - renderHook(() => useAlertDataViews([AlertConsumers.SIEM]), { - wrapper, - }); - - await waitFor(() => expect(fetchAlertIndexNames).toHaveBeenCalledTimes(1)); - expect(fetchAlertFields).toHaveBeenCalledTimes(0); - }); - - it('Do not fetch anything if security and o11y featureIds are mixed together', async () => { - const { result } = renderHook( - () => useAlertDataViews([AlertConsumers.SIEM, AlertConsumers.LOGS]), - { - wrapper, - } - ); - - await waitFor(() => - expect(result.current).toEqual({ - loading: false, - dataview: undefined, - }) - ); - expect(fetchAlertIndexNames).toHaveBeenCalledTimes(0); - expect(fetchAlertFields).toHaveBeenCalledTimes(0); - }); - - it('if fetch throws error return no data', async () => { - fetchAlertIndexNames.mockRejectedValue('error'); - - const { result } = renderHook(() => useAlertDataViews(observabilityAlertFeatureIds), { - wrapper, - }); - - await waitFor(() => - expect(result.current).toEqual({ - loading: false, - dataview: undefined, - }) - ); - }); -}); diff --git a/x-pack/plugins/cases/public/components/system_actions/hooks/use_alert_data_view.ts b/x-pack/plugins/cases/public/components/system_actions/hooks/use_alert_data_view.ts deleted file mode 100644 index 863b07949af9d..0000000000000 --- a/x-pack/plugins/cases/public/components/system_actions/hooks/use_alert_data_view.ts +++ /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 { i18n } from '@kbn/i18n'; -import type { DataView } from '@kbn/data-views-plugin/common'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import type { ValidFeatureId } from '@kbn/rule-data-utils'; -import { AlertConsumers } from '@kbn/rule-data-utils'; -import { useEffect, useMemo, useState } from 'react'; -import { useQuery } from '@tanstack/react-query'; -import type { TriggersAndActionsUiServices } from '@kbn/triggers-actions-ui-plugin/public'; -import { fetchAlertIndexNames } from './alert_index'; -import { fetchAlertFields } from './alert_fields'; - -export interface UserAlertDataViews { - dataViews?: DataView[]; - loading: boolean; -} - -export function useAlertDataViews(featureIds: ValidFeatureId[]): UserAlertDataViews { - const { - http, - data: dataService, - notifications: { toasts }, - } = useKibana<TriggersAndActionsUiServices>().services; - const [dataViews, setDataViews] = useState<DataView[] | undefined>(undefined); - const features = featureIds.sort().join(','); - const isOnlySecurity = featureIds.length === 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasSecurityAndO11yFeatureIds = - featureIds.length > 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasNoSecuritySolution = - featureIds.length > 0 && !isOnlySecurity && !hasSecurityAndO11yFeatureIds; - - const queryIndexNameFn = () => { - return fetchAlertIndexNames({ http, features }); - }; - - const queryAlertFieldsFn = () => { - return fetchAlertFields({ http, featureIds }); - }; - - const onErrorFn = () => { - toasts.addDanger( - i18n.translate('xpack.cases.systemActions.useAlertDataView.useAlertDataMessage', { - defaultMessage: 'Unable to load alert data view', - }) - ); - }; - - const { - data: indexNames, - isSuccess: isIndexNameSuccess, - isInitialLoading: isIndexNameInitialLoading, - isLoading: isIndexNameLoading, - } = useQuery({ - queryKey: ['loadAlertIndexNames', features], - queryFn: queryIndexNameFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - enabled: featureIds.length > 0 && !hasSecurityAndO11yFeatureIds, - }); - - const { - data: alertFields, - isSuccess: isAlertFieldsSuccess, - isInitialLoading: isAlertFieldsInitialLoading, - isLoading: isAlertFieldsLoading, - } = useQuery({ - queryKey: ['loadAlertFields', features], - queryFn: queryAlertFieldsFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - enabled: hasNoSecuritySolution, - }); - - useEffect(() => { - return () => { - dataViews?.map((dv) => dataService.dataViews.clearInstanceCache(dv.id)); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dataViews]); - - // FUTURE ENGINEER this useEffect is for security solution user since - // we are using the user privilege to access the security alert index - useEffect(() => { - async function createDataView() { - const localDataview = await dataService?.dataViews.create({ - title: (indexNames ?? []).join(','), - allowNoIndex: true, - }); - setDataViews([localDataview]); - } - - if (isOnlySecurity && isIndexNameSuccess) { - createDataView(); - } - }, [dataService?.dataViews, indexNames, isIndexNameSuccess, isOnlySecurity]); - - // FUTURE ENGINEER this useEffect is for o11y and stack solution user since - // we are using the kibana user privilege to access the alert index - useEffect(() => { - if ( - indexNames && - alertFields && - !isOnlySecurity && - isAlertFieldsSuccess && - isIndexNameSuccess - ) { - setDataViews([ - { - title: (indexNames ?? []).join(','), - fieldFormatMap: {}, - fields: (alertFields ?? [])?.map((field) => { - return { - ...field, - ...(field.esTypes && field.esTypes.includes('flattened') ? { type: 'string' } : {}), - }; - }), - }, - ] as unknown as DataView[]); - } - }, [ - alertFields, - dataService?.dataViews, - indexNames, - isIndexNameSuccess, - isOnlySecurity, - isAlertFieldsSuccess, - ]); - - return useMemo( - () => ({ - dataViews, - loading: - featureIds.length === 0 || hasSecurityAndO11yFeatureIds - ? false - : isOnlySecurity - ? isIndexNameInitialLoading || isIndexNameLoading - : isIndexNameInitialLoading || - isIndexNameLoading || - isAlertFieldsInitialLoading || - isAlertFieldsLoading, - }), - [ - dataViews, - featureIds.length, - hasSecurityAndO11yFeatureIds, - isOnlySecurity, - isIndexNameInitialLoading, - isIndexNameLoading, - isAlertFieldsInitialLoading, - isAlertFieldsLoading, - ] - ); -} diff --git a/x-pack/plugins/cases/tsconfig.json b/x-pack/plugins/cases/tsconfig.json index 6bbc5cfd4e421..fbe05134b5bfd 100644 --- a/x-pack/plugins/cases/tsconfig.json +++ b/x-pack/plugins/cases/tsconfig.json @@ -73,9 +73,9 @@ "@kbn/datemath", "@kbn/core-logging-server-mocks", "@kbn/core-logging-browser-mocks", - "@kbn/data-views-plugin", "@kbn/core-http-router-server-internal", "@kbn/presentation-publishing", + "@kbn/alerts-ui-shared", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 3c08632a262e5..053dcc4867bdd 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -85,8 +85,6 @@ export const LATEST_VULNERABILITIES_INDEX_DEFAULT_NS = 'logs-cloud_security_posture.vulnerabilities_latest-default'; export const LATEST_VULNERABILITIES_RETENTION_POLICY = '3d'; -export const DATA_VIEW_INDEX_PATTERN = 'logs-*'; - export const SECURITY_DEFAULT_DATA_VIEW_ID = 'security-solution-default'; export const ALERTS_INDEX_PATTERN = '.alerts-security.alerts-*'; @@ -157,9 +155,6 @@ export const POSTURE_TYPES: { [x: string]: PostureTypes } = { [POSTURE_TYPE_ALL]: POSTURE_TYPE_ALL, }; -export const VULNERABILITIES = 'vulnerabilities'; -export const CONFIGURATIONS = 'configurations'; - export const VULNERABILITIES_SEVERITY: Record<VulnSeverity, VulnSeverity> = { LOW: 'LOW', MEDIUM: 'MEDIUM', @@ -168,8 +163,6 @@ export const VULNERABILITIES_SEVERITY: Record<VulnSeverity, VulnSeverity> = { UNKNOWN: 'UNKNOWN', }; -export const VULNERABILITIES_ENUMERATION = 'CVE'; - export const AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP: AwsCredentialsTypeFieldMap = { assume_role: ['role_arn'], direct_access_keys: ['access_key_id', 'secret_access_key'], @@ -209,7 +202,6 @@ export const AZURE_CREDENTIALS_TYPE_TO_FIELDS_MAP = { manual: [], }; -export const CLOUD_FORMATION_STACK_NAME = 'Elastic-Cloud-Security-Posture-Management'; export const TEMPLATE_URL_ACCOUNT_TYPE_ENV_VAR = 'ACCOUNT_TYPE'; export const ORGANIZATION_ACCOUNT = 'organization-account'; diff --git a/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_safe_kspm_cluster_id_runtime_mapping.ts b/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_safe_kspm_cluster_id_runtime_mapping.ts deleted file mode 100644 index eed3eba2f7220..0000000000000 --- a/x-pack/plugins/cloud_security_posture/common/runtime_mappings/get_safe_kspm_cluster_id_runtime_mapping.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; - -/** - * Creates the `safe_posture_type` runtime field with the value of either - * `kspm` or `cspm` based on the value of `rule.benchmark.posture_type` - */ -export const getSafeKspmClusterIdRuntimeMapping = (): MappingRuntimeFields => ({ - safe_kspm_cluster_id: { - type: 'keyword', - script: { - source: ` - def orchestratorIdAvailable = doc.containsKey("orchestrator.cluster.id") && - !doc["orchestrator.cluster.id"].empty; - def clusterIdAvailable = doc.containsKey("cluster_id") && - !doc["cluster_id"].empty; - - if (orchestratorIdAvailable) { - emit(doc["orchestrator.cluster.id"].value); - } else if (clusterIdAvailable) { - emit(doc["cluster_id"].value); - } - `, - }, - }, -}); 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 index 85c38c50022b8..a00bf1a8077e6 100644 --- a/x-pack/plugins/cloud_security_posture/common/types/rules/v3.ts +++ b/x-pack/plugins/cloud_security_posture/common/types/rules/v3.ts @@ -18,8 +18,6 @@ export type CspBenchmarkRuleMetadata = TypeOf<typeof cspBenchmarkRuleMetadataSch export type CspBenchmarkRule = TypeOf<typeof cspBenchmarkRuleSchema>; -export type PageUrlParams = Record<'policyId' | 'packagePolicyId', string>; - export const cspBenchmarkRuleMetadataSchema = schema.object({ audit: schema.string(), benchmark: schema.object({ diff --git a/x-pack/plugins/cloud_security_posture/common/types_old.ts b/x-pack/plugins/cloud_security_posture/common/types_old.ts index 6b290979a97f5..f77ac4678a526 100644 --- a/x-pack/plugins/cloud_security_posture/common/types_old.ts +++ b/x-pack/plugins/cloud_security_posture/common/types_old.ts @@ -36,10 +36,6 @@ export type AzureCredentialsType = | 'service_principal_with_client_username_and_password' | 'managed_identity'; -export type AzureCredentialsTypeFieldMap = { - [key in AzureCredentialsType]: string[]; -}; - export type Evaluation = 'passed' | 'failed' | 'NA'; export type PostureTypes = 'cspm' | 'kspm' | 'vuln_mgmt' | 'all'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/component/multi_select_filter.tsx b/x-pack/plugins/cloud_security_posture/public/common/component/multi_select_filter.tsx index 6eece8a31c5e8..ecc5edcbf2e73 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/component/multi_select_filter.tsx +++ b/x-pack/plugins/cloud_security_posture/public/common/component/multi_select_filter.tsx @@ -27,17 +27,6 @@ type FilterOption<T extends string, K extends string = string> = EuiSelectableOp label: T; }>; -export type { FilterOption as MultiSelectFilterOption }; - -export const mapToMultiSelectOption = <T extends string>(options: T[]) => { - return options.map((option) => { - return { - key: option, - label: option, - }; - }); -}; - const fromRawOptionsToEuiSelectableOptions = <T extends string, K extends string>( options: Array<FilterOption<T, K>>, selectedOptionKeys: string[] 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 73934752e819a..8054917ba7462 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -40,11 +40,8 @@ export const DEFAULT_VISIBLE_ROWS_PER_PAGE = 25; export const LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY = 'cloudPosture:dataTable:pageSize'; export const LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY = 'cloudPosture:dataTable:columns'; -export const LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY = 'cloudPosture:findings:pageSize'; export const LOCAL_STORAGE_PAGE_SIZE_BENCHMARK_KEY = 'cloudPosture:benchmark:pageSize'; export const LOCAL_STORAGE_PAGE_SIZE_RULES_KEY = 'cloudPosture:rules:pageSize'; -export const LOCAL_STORAGE_DASHBOARD_CLUSTER_SORT_KEY = - 'cloudPosture:complianceDashboard:clusterSort'; export const LOCAL_STORAGE_DASHBOARD_BENCHMARK_SORT_KEY = 'cloudPosture:complianceDashboard:benchmarkSort'; export const LOCAL_STORAGE_FINDINGS_LAST_SELECTED_TAB_KEY = 'cloudPosture:findings:lastSelectedTab'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/utils.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/utils.ts index 6628cc7711a82..98270ffee59bf 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_cloud_posture_data_table/utils.ts @@ -5,28 +5,6 @@ * 2.0. */ -import type { EuiBasicTableProps, Pagination } from '@elastic/eui'; - -type TablePagination = NonNullable<EuiBasicTableProps<object>['pagination']>; - -export const getPaginationTableParams = ( - params: TablePagination & Pick<Required<TablePagination>, 'pageIndex' | 'pageSize'>, - pageSizeOptions = [10, 25, 100], - showPerPageOptions = true -): Required<TablePagination> => ({ - ...params, - pageSizeOptions, - showPerPageOptions, -}); - -export const getPaginationQuery = ({ - pageIndex, - pageSize, -}: Required<Pick<Pagination, 'pageIndex' | 'pageSize'>>) => ({ - from: pageIndex * pageSize, - size: pageSize, -}); - export const getDefaultQuery = ({ query, filters }: any): any => ({ query, filters, diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_page_slice.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_page_slice.ts deleted file mode 100644 index e089724b25909..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_page_slice.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { useMemo } from 'react'; - -/** - * @description given an array index and page size, returns a slice of said array. - */ -export const usePageSlice = (data: any[] | undefined, pageIndex: number, pageSize: number) => { - return useMemo(() => { - if (!data) { - return []; - } - - const cursor = pageIndex * pageSize; - return data.slice(cursor, cursor + pageSize); - }, [data, pageIndex, pageSize]); -}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/types.ts b/x-pack/plugins/cloud_security_posture/public/common/types.ts index f2881c1798883..d0d491c256e0e 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/types.ts @@ -6,9 +6,6 @@ */ import type { Criteria } from '@elastic/eui'; import type { BoolQuery, Filter, Query, EsQueryConfig } from '@kbn/es-query'; -import { CspFinding } from '../../common/schemas/csp_finding'; - -export type FindingsGroupByKind = 'default' | 'resource'; export interface FindingsBaseURLQuery { query: Query; @@ -33,11 +30,6 @@ export interface FindingsBaseEsQuery { }; } -export interface CspFindingsQueryData { - page: CspFinding[]; - total: number; -} - export type Sort<T> = NonNullable<Criteria<T>['sort']>; interface RuleSeverityMapping { 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 deleted file mode 100644 index cd0a413648bed..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/common/utils/get_enabled_csp_integration_details.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { PackagePolicy } from '@kbn/fleet-plugin/common'; -import type { PostureInput } from '../../../common/types_old'; -import { SUPPORTED_CLOUDBEAT_INPUTS } from '../../../common/constants'; -import { cloudPostureIntegrations, type CloudPostureIntegrations } from '../constants'; - -const isPolicyTemplate = (name: unknown): name is keyof CloudPostureIntegrations => - typeof name === 'string' && name in cloudPostureIntegrations; - -export const getEnabledCspIntegrationDetails = (packageInfo?: PackagePolicy) => { - const enabledInput = packageInfo?.inputs.find((input) => input.enabled); - - // Check for valid and support input - if ( - !enabledInput || - !isPolicyTemplate(enabledInput.policy_template) || - !SUPPORTED_CLOUDBEAT_INPUTS.includes(enabledInput.type as PostureInput) - ) - return null; - - const integration = cloudPostureIntegrations[enabledInput.policy_template]; - const enabledIntegrationOption = integration.options.find( - (option) => option.type === enabledInput.type - ); - - return { integration, enabledIntegrationOption }; -}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/utils/get_limit_properties.test.ts b/x-pack/plugins/cloud_security_posture/public/common/utils/get_limit_properties.test.ts deleted file mode 100644 index f62062ba37f4d..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/common/utils/get_limit_properties.test.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { getLimitProperties } from './get_limit_properties'; - -describe('getLimitProperties', () => { - it('less items than limit', () => { - const { limitedTotalItemCount, isLastLimitedPage } = getLimitProperties(200, 500, 100, 1); - - expect(limitedTotalItemCount).toBe(200); - expect(isLastLimitedPage).toBe(false); - }); - - it('more items than limit', () => { - const { limitedTotalItemCount, isLastLimitedPage } = getLimitProperties(600, 500, 100, 4); - - expect(limitedTotalItemCount).toBe(500); - expect(isLastLimitedPage).toBe(true); - }); - - it('per page calculations are correct', () => { - const { limitedTotalItemCount, isLastLimitedPage } = getLimitProperties(600, 500, 25, 19); - - expect(limitedTotalItemCount).toBe(500); - expect(isLastLimitedPage).toBe(true); - }); -}); diff --git a/x-pack/plugins/cloud_security_posture/public/common/utils/get_limit_properties.ts b/x-pack/plugins/cloud_security_posture/public/common/utils/get_limit_properties.ts deleted file mode 100644 index f09990bc3f854..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/common/utils/get_limit_properties.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo } from 'react'; -import { MAX_FINDINGS_TO_LOAD } from '../constants'; - -export const getLimitProperties = ( - totalItems: number, - maxItems: number, - pageSize: number, - pageIndex: number -): { isLastLimitedPage: boolean; limitedTotalItemCount: number } => { - const limitItems = totalItems > maxItems; - const limitedTotalItemCount = limitItems ? maxItems : totalItems; - const lastLimitedPage = Math.ceil(limitedTotalItemCount / pageSize); - const isLastPage = lastLimitedPage === pageIndex + 1; - const isLastLimitedPage = limitItems && isLastPage; - - return { isLastLimitedPage, limitedTotalItemCount }; -}; - -export const useLimitProperties = ({ - total, - pageIndex, - pageSize, -}: { - total?: number; - pageSize: number; - pageIndex: number; -}) => - useMemo( - () => getLimitProperties(total || 0, MAX_FINDINGS_TO_LOAD, pageSize, pageIndex), - [total, pageIndex, pageSize] - ); 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 index 867fc1af4d9ea..8a5587cf16622 100644 --- 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 @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiButton, EuiCallOut, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiButton, EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import semverCompare from 'semver/functions/compare'; import semverValid from 'semver/functions/valid'; @@ -36,106 +36,6 @@ import { AwsCredentialTypeSelector, } from './aws_credentials_form'; -const CLOUD_FORMATION_EXTERNAL_DOC_URL = - 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html'; - -export const CloudFormationCloudCredentialsGuide = ({ - isOrganization, -}: { - isOrganization?: boolean; -}) => { - return ( - <EuiText> - <p> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.guide.description" - defaultMessage="CloudFormation will create all the necessary resources to evaluate the security posture of your AWS environment. {learnMore}." - values={{ - learnMore: ( - <EuiLink - href={CLOUD_FORMATION_EXTERNAL_DOC_URL} - target="_blank" - rel="noopener nofollow noreferrer" - data-test-subj="externalLink" - > - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.guide.learnMoreLinkText" - defaultMessage="Learn more about CloudFormation" - /> - </EuiLink> - ), - }} - /> - </p> - <EuiText size="s" color="subdued"> - <ol> - {isOrganization ? ( - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.guide.steps.organizationLogin" - defaultMessage="Log in as an admin in the management account of the AWS Organization you want to onboard" - /> - </li> - ) : ( - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.guide.steps.login" - defaultMessage="Log in as an admin in the AWS account you want to onboard" - /> - </li> - )} - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.guide.steps.launch" - defaultMessage="Click the Launch CloudFormation button below." - /> - </li> - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.steps.region" - defaultMessage="(Optional) Change the Amazon region in the upper right corner to the region you want to deploy your stack to" - /> - </li> - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.gsteps.accept" - defaultMessage="Tick the checkbox under capabilities in the opened CloudFormation stack review form: {acknowledge}" - values={{ - acknowledge: ( - <strong> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.steps.accept.acknowledge" - defaultMessage="I acknowledge that AWS CloudFormation might create IAM resources." - /> - </strong> - ), - }} - /> - </li> - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.steps.create" - defaultMessage="Click Create stack." - /> - </li> - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.steps.stackStatus" - defaultMessage="Once stack status is CREATE_COMPLETE then click the Ouputs tab" - /> - </li> - <li> - <FormattedMessage - id="xpack.csp.agentlessForm.cloudFormation.steps.credentials" - defaultMessage="Copy Access Key Id and Secret Access Key then paste the credentials below" - /> - </li> - </ol> - </EuiText> - </EuiText> - ); -}; - export const AwsCredentialsFormAgentless = ({ input, newPolicy, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/test_subjects.ts index 2a325ee225128..6c1a73ed735aa 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/test_subjects.ts @@ -5,7 +5,6 @@ * 2.0. */ -export const MISSING_FINDINGS_NO_DATA_CONFIG = 'missing-findings-no-data-config'; export const DASHBOARD_CONTAINER = 'dashboard-container'; export const DASHBOARD_SUMMARY_CONTAINER = 'dashboard-summary-section'; export const KUBERNETES_DASHBOARD_CONTAINER = 'kubernetes-dashboard-container'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx index ddb3c757b420e..535a465917d44 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx @@ -185,7 +185,7 @@ const FindingsCountComponent = ({ bucket }: { bucket: RawBucket<FindingsGrouping <EuiToolTip content={bucket.doc_count}> <EuiBadge css={css` - margin-left: ${euiTheme.size.s}}; + margin-left: ${euiTheme.size.s}; `} color="hollow" data-test-subj={FINDINGS_GROUPING_COUNTER} @@ -208,7 +208,7 @@ const ComplianceBarComponent = ({ bucket }: { bucket: RawBucket<FindingsGrouping size="l" overrideCss={css` width: 104px; - margin-left: ${euiTheme.size.s}}; + margin-left: ${euiTheme.size.s}; `} totalFailed={totalFailed} totalPassed={totalPassed} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts index a9bb7540ab12e..d609617824269 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings.ts @@ -8,7 +8,6 @@ import { useInfiniteQuery } from '@tanstack/react-query'; import { number } from 'io-ts'; import { lastValueFrom } from 'rxjs'; import type { IKibanaSearchResponse, IKibanaSearchRequest } from '@kbn/search-types'; -import type { Pagination } from '@elastic/eui'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { buildDataTableRecord } from '@kbn/discover-utils'; import { EsHitRecord } from '@kbn/discover-utils/types'; @@ -32,11 +31,6 @@ interface UseFindingsOptions extends FindingsBaseEsQuery { pageSize: number; } -export interface FindingsGroupByNoneQuery { - pageIndex: Pagination['pageIndex']; - sort: any; -} - type LatestFindingsRequest = IKibanaSearchRequest<estypes.SearchRequest>; type LatestFindingsResponse = IKibanaSearchResponse< estypes.SearchResponse<CspFinding, FindingsAggs> 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 56ca9687551d8..811abe1bd1ccc 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 @@ -6,16 +6,8 @@ */ import React from 'react'; import { css } from '@emotion/react'; -import { - EuiHealth, - EuiBadge, - EuiSpacer, - EuiFlexGroup, - useEuiTheme, - EuiTextColor, -} from '@elastic/eui'; +import { EuiHealth, EuiBadge, EuiSpacer, EuiFlexGroup, useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -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'; @@ -35,31 +27,6 @@ const I18N_FAILED_FINDINGS = i18n.translate('xpack.csp.findings.distributionBar. defaultMessage: 'Failed Findings', }); -export const CurrentPageOfTotal = ({ - pageEnd, - pageStart, - total, - type, -}: { - pageEnd: number; - pageStart: number; - total: number; - type: string; -}) => ( - <EuiTextColor color="subdued"> - <FormattedMessage - id="xpack.csp.findings.distributionBar.showingPageOfTotalLabel" - defaultMessage="Showing {pageStart}-{pageEnd} of {total} {type}" - values={{ - pageStart: <b>{pageStart}</b>, - pageEnd: <b>{pageEnd}</b>, - total: <b>{getAbbreviatedNumber(total)}</b>, - type, - }} - /> - </EuiTextColor> -); - export const FindingsDistributionBar = (props: Props) => ( <div> <Counters {...props} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_group_by_selector.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_group_by_selector.tsx deleted file mode 100644 index d8d8768df0cf1..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_group_by_selector.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import React, { useMemo } from 'react'; -import { EuiComboBox, EuiFormLabel, type EuiComboBoxOptionOption } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { useHistory } from 'react-router-dom'; -import { i18n } from '@kbn/i18n'; -import type { FindingsGroupByKind } from '../../../common/types'; -import { findingsNavigation } from '../../../common/navigation/constants'; -import * as TEST_SUBJECTS from '../test_subjects'; - -const getGroupByOptions = (): Array<EuiComboBoxOptionOption<FindingsGroupByKind>> => [ - { - value: 'default', - label: i18n.translate('xpack.csp.findings.groupBySelector.groupByNoneLabel', { - defaultMessage: 'None', - }), - }, - { - value: 'resource', - label: i18n.translate('xpack.csp.findings.groupBySelector.groupByResourceIdLabel', { - defaultMessage: 'Resource', - }), - }, -]; - -interface Props { - type: FindingsGroupByKind; - pathnameHandler?: (opts: Array<EuiComboBoxOptionOption<FindingsGroupByKind>>) => string; -} - -const getFindingsGroupPath = (opts: Array<EuiComboBoxOptionOption<FindingsGroupByKind>>) => { - const [firstOption] = opts; - - switch (firstOption?.value) { - case 'resource': - return findingsNavigation.findings_by_resource.path; - case 'default': - default: - return findingsNavigation.findings_default.path; - } -}; - -export const FindingsGroupBySelector = ({ - type, - pathnameHandler = getFindingsGroupPath, -}: Props) => { - const groupByOptions = useMemo(getGroupByOptions, []); - const history = useHistory(); - - const onChange = (options: Array<EuiComboBoxOptionOption<FindingsGroupByKind>>) => - history.push({ pathname: pathnameHandler(options) }); - - return ( - <EuiComboBox - data-test-subj={TEST_SUBJECTS.FINDINGS_GROUP_BY_SELECTOR} - prepend={<GroupByLabel />} - singleSelection={{ asPlainText: true }} - options={groupByOptions} - selectedOptions={groupByOptions.filter((o) => o.value === type)} - onChange={onChange} - isClearable={false} - compressed - /> - ); -}; - -const GroupByLabel = () => ( - <EuiFormLabel> - <FormattedMessage - id="xpack.csp.findings.groupBySelector.groupByLabel" - defaultMessage="Group by" - /> - </EuiFormLabel> -); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/test_subjects.ts index b8a670e8ba58b..d27a7739ab9a9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/test_subjects.ts @@ -6,33 +6,11 @@ */ export const FINDINGS_FLYOUT = 'findings_flyout'; -export const FINDINGS_TABLE_EXPAND_COLUMN = 'findings_table_expand_column'; -export const FINDINGS_TABLE = 'findings_table'; -export const FINDINGS_CONTAINER = 'findings_container'; -export const FINDINGS_BY_RESOURCE_CONTAINER = 'findings_by_resource_container'; -export const FINDINGS_BY_RESOURCE_TABLE_RESOURCE_ID_COLUMN = - 'findings_by_resource_table_resource_id_column'; -export const FINDINGS_BY_RESOURCE_TABLE = 'findings_by_resource_table'; -export const getFindingsByResourceTableRowTestId = (id: string) => - `findings_resource_table_row_${id}`; export const LATEST_FINDINGS_CONTAINER = 'latest_findings_container'; export const LATEST_FINDINGS_TABLE = 'latest_findings_table'; -export const FINDINGS_GROUP_BY_SELECTOR = 'findings_group_by_selector'; export const FINDINGS_GROUPING_COUNTER = 'findings_grouping_counter'; -export const getFindingsTableRowTestId = (id: string) => `findings_table_row_${id}`; -export const getFindingsTableCellTestId = (columnId: string, rowId: string) => - `findings_table_cell_${columnId}_${rowId}`; - -export const FINDINGS_TABLE_CELL_ADD_FILTER = 'findings_table_cell_add_filter'; -export const FINDINGS_TABLE_CELL_ADD_NEGATED_FILTER = 'findings_table_cell_add_negated_filter'; - -export const RESOURCES_FINDINGS_CONTAINER = 'resources_findings_container'; -export const RESOURCES_FINDINGS_TABLE = 'resource_findings_table'; -export const getResourceFindingsTableRowTestId = (id: string) => - `resource_findings_table_row_${id}`; - export const FINDINGS_MISCONFIGS_FLYOUT_DESCRIPTION_LIST = 'misconfigs-findings-flyout-description-list'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/generate_findings_tags.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/generate_findings_tags.ts deleted file mode 100644 index 66da177e1cea8..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/generate_findings_tags.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 { CspFinding } from '../../../../common/schemas/csp_finding'; - -const CSP_RULE_TAG = 'Cloud Security'; -const CNVM_RULE_TAG_USE_CASE = 'Use Case: Configuration Audit'; -const CNVM_RULE_TAG_DATA_SOURCE_PREFIX = 'Data Source: '; - -const STATIC_RULE_TAGS = [CSP_RULE_TAG, CNVM_RULE_TAG_USE_CASE]; - -export const generateFindingsTags = (finding: CspFinding) => { - return [STATIC_RULE_TAGS] - .concat(finding.rule.tags) - .concat( - finding.rule.benchmark.posture_type - ? [ - `${CNVM_RULE_TAG_DATA_SOURCE_PREFIX}${finding.rule.benchmark.posture_type.toUpperCase()}`, - ] - : [] - ) - .concat( - finding.rule.benchmark.posture_type === 'cspm' ? ['Domain: Cloud'] : ['Domain: Container'] - ) - .flat(); -}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/utils.ts index 3e0277d7cd4c1..67b37ad1001fd 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/utils/utils.ts @@ -6,19 +6,8 @@ */ import type { estypes } from '@elastic/elasticsearch'; -import { EuiThemeComputed } from '@elastic/eui'; -import type { CspFinding } from '../../../../common/schemas/csp_finding'; export { getFilters } from './get_filters'; -export const getFindingsPageSizeInfo = ({ - currentPageSize, - pageIndex, - pageSize, -}: Record<'pageIndex' | 'pageSize' | 'currentPageSize', number>) => ({ - pageStart: pageIndex * pageSize + 1, - pageEnd: pageIndex * pageSize + currentPageSize, -}); - export const getFindingsCountAggQuery = () => ({ count: { terms: { field: 'result.evaluation' } }, }); @@ -34,14 +23,3 @@ export const getAggregationCount = ( failed: failed?.doc_count || 0, }; }; - -const isSelectedRow = (row: CspFinding, selected?: CspFinding) => - row.resource.id === selected?.resource.id && row.rule.id === selected?.rule.id; - -export const getSelectedRowStyle = ( - theme: EuiThemeComputed, - row: CspFinding, - selected?: CspFinding -): React.CSSProperties => ({ - background: isSelectedRow(row, selected) ? theme.colors.highlight : undefined, -}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx index 7abcd0c37060b..66829ee739010 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules.test.tsx @@ -22,9 +22,6 @@ import { useLicenseManagementLocatorApi } from '../../common/api/use_license_man import { useCspBenchmarkIntegrationsV2 } from '../benchmarks/use_csp_benchmark_integrations'; import * as TEST_SUBJECTS from './test_subjects'; -jest.mock('./use_csp_integration', () => ({ - useCspIntegrationInfo: jest.fn(), -})); jest.mock('../../common/api/use_setup_status_api'); jest.mock('../../common/api/use_license_management_locator_api'); jest.mock('../../common/hooks/use_subscription_status'); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx index 83745e5f5d113..fc714263f38be 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table_header.tsx @@ -42,8 +42,6 @@ export const RULES_SELECT_ALL_RULES = 'select-all-rules-button'; export const RULES_CLEAR_ALL_RULES_SELECTION = 'clear-rules-selection-button'; export const RULES_DISABLED_FILTER = 'rules-disabled-filter'; export const RULES_ENABLED_FILTER = 'rules-enabled-filter'; -export const CIS_SECTION_FILTER = 'cis-section-filter'; -export const RULE_NUMBER_FILTER = 'rule-number-filter'; interface RulesTableToolbarProps { search: (value: string) => void; 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 43b209c6ce7d4..eb723c4e7894a 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 @@ -6,9 +6,7 @@ */ export const CSP_RULES_CONTAINER = 'csp_rules_container'; -export const CSP_RULES_SHARED_VALUES = 'csp_rules_shared_values'; -export const CSP_RULES_TABLE_ITEM_SWITCH = 'csp_rules_table_item_switch'; -export const CSP_RULES_SAVE_BUTTON = 'csp_rules_table_save_button'; + 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'; 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 index d580a7719ed0a..fd8c5bf794935 100644 --- 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 @@ -20,7 +20,6 @@ export type RulesQuery = Pick< FindCspBenchmarkRuleRequest, 'section' | 'search' | 'page' | 'perPage' | 'ruleNumber' | 'sortField' | 'sortOrder' >; -export type RulesQueryResult = ReturnType<typeof useFindCspBenchmarkRule>; export const useFindCspBenchmarkRule = ( { search, page, perPage, section, ruleNumber, sortField, sortOrder }: RulesQuery, 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 deleted file mode 100644 index 45d4743490e3f..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx +++ /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 { - type CopyAgentPolicyResponse, - type GetOnePackagePolicyResponse, - packagePolicyRouteService, - agentPolicyRouteService, - API_VERSIONS, -} from '@kbn/fleet-plugin/common'; -import { useKibana } from '../../common/hooks/use_kibana'; -import { PageUrlParams } from '../../../common/types/rules/v3'; - -export const useCspIntegrationInfo = ({ packagePolicyId, policyId }: PageUrlParams) => { - const { http } = useKibana().services; - - return useQuery(['cspBenchmarkRuleInfo', { packagePolicyId, policyId }], () => - Promise.all([ - http - .get<GetOnePackagePolicyResponse>(packagePolicyRouteService.getInfoPath(packagePolicyId), { - version: API_VERSIONS.public.v1, - }) - .then((response) => response.item), - http - .get<CopyAgentPolicyResponse>(agentPolicyRouteService.getInfoPath(policyId), { - version: API_VERSIONS.public.v1, - }) - .then((response) => response.item), - ]) - ); -}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx index 9c88ef2ab85d0..351cecd83d502 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx @@ -150,7 +150,7 @@ const VulnerabilitiesCountComponent = ({ <EuiToolTip content={bucket.doc_count}> <EuiBadge css={css` - margin-left: ${euiTheme.size.s}}; + margin-left: ${euiTheme.size.s}; `} color="hollow" data-test-subj={VULNERABILITIES_GROUPING_COUNTER} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/test_subjects.ts index 8ad512f8a41ee..d57f86171e4b4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/test_subjects.ts @@ -5,12 +5,10 @@ * 2.0. */ -export const FINDINGS_VULNERABILITY_FLYOUT = 'vulnerability_findings_flyout'; export const FINDINGS_VULNERABILITY_FLYOUT_DESCRIPTION_LIST = 'vulnerability-flyout-description-list'; export const JSON_TAB_VULNERABILITY_FLYOUT = 'vulnerability_json_tab_flyout'; export const OVERVIEW_TAB_VULNERABILITY_FLYOUT = 'vulnerability_overview_tab_flyout'; -export const SEVERITY_STATUS_VULNERABILITY_FLYOUT = 'vulnerability_severity_status_flyout'; export const TAB_ID_VULNERABILITY_FLYOUT = (tabId: string) => `vulnerability-finding-flyout-tab-${tabId}`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts index cc5392bd6da66..2864ff7f29005 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/translations.ts @@ -9,18 +9,6 @@ import { i18n } from '@kbn/i18n'; import { VULNERABILITY_GROUPING_OPTIONS } from '../../common/constants'; -export const FILTER_IN = i18n.translate('xpack.csp.vulnerabilities.table.filterIn', { - defaultMessage: 'Filter in', -}); -export const FILTER_OUT = i18n.translate('xpack.csp.vulnerabilities.table.filterOut', { - defaultMessage: 'Filter out', -}); -export const SEARCH_BAR_PLACEHOLDER = i18n.translate( - 'xpack.csp.vulnerabilities.searchBar.placeholder', - { - defaultMessage: 'Search vulnerabilities (eg. vulnerability.severity : "CRITICAL" )', - } -); export const VULNERABILITIES = i18n.translate('xpack.csp.vulnerabilities', { defaultMessage: 'Vulnerabilities', }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts index 14e9fbae28d41..e0c97ce6ff76d 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { VectorScoreBase, CspVulnerabilityFinding } from '../../../common/schemas'; +import { VectorScoreBase } from '../../../common/schemas'; export type Vendor = 'NVD' | 'Red Hat' | 'GHSA'; @@ -19,29 +19,3 @@ export interface Vector { vector: string; score: number | undefined; } - -export interface VulnerabilitiesQueryData { - page: CspVulnerabilityFinding[]; - total: number; -} - -export interface VulnerabilitiesByResourceQueryData { - page: Array<{ - resource: { - id: string; - name: string; - }; - cloud: { - region: string; - }; - vulnerabilities_count: number; - severity_map: { - critical: number; - high: number; - medium: number; - low: number; - }; - }>; - total: number; - total_vulnerabilities: number; -} diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/custom_sort_script.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/custom_sort_script.ts index d184b0ed568a4..780cd539305b3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/custom_sort_script.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/utils/custom_sort_script.ts @@ -5,43 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; - -export const severitySchemaConfig = { - type: 'severitySchema', - detector() { - return 0; // this schema is always explicitly defined - }, - sortTextAsc: i18n.translate('xpack.csp.vulnerabilityTable.column.sortAscending', { - defaultMessage: 'Low -> Critical', - }), - sortTextDesc: i18n.translate('xpack.csp.vulnerabilityTable.column.sortDescending', { - defaultMessage: 'Critical -> Low', - }), - icon: 'dot', - color: '', -}; - -export const severitySortScript = (direction: string) => ({ - _script: { - type: 'number', - script: { - lang: 'painless', - inline: - "if(doc.containsKey('vulnerability.severity') && !doc['vulnerability.severity'].empty && doc['vulnerability.severity'].size()!=0 && doc['vulnerability.severity'].value!=null && params.scores.containsKey(doc['vulnerability.severity'].value)) { return params.scores[doc['vulnerability.severity'].value];} return 0;", - params: { - scores: { - LOW: 1, - MEDIUM: 2, - HIGH: 3, - CRITICAL: 4, - }, - }, - }, - order: direction, - }, -}); - /** * Generates Painless sorting in case-insensitive manner */ diff --git a/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts b/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts index cb67ab59333e8..1fec1c76430eb 100644 --- a/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts +++ b/x-pack/plugins/cloud_security_posture/public/test/fixtures/findings_fixture.ts @@ -4,66 +4,3 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { EcsEvent } from '@elastic/ecs'; -import Chance from 'chance'; -import { CspFinding } from '../../../common/schemas/csp_finding'; - -const chance = new Chance(); - -export const getFindingsFixture = (): CspFinding & { id: string } => ({ - cluster_id: chance.guid(), - id: chance.word(), - result: { - expected: { - source: {}, - }, - evaluation: chance.weighted(['passed', 'failed'], [0.5, 0.5]), - evidence: { - filemode: chance.word(), - }, - }, - rule: { - audit: chance.paragraph(), - benchmark: { - name: 'CIS Kubernetes', - version: '1.6.0', - id: 'cis_k8s', - rule_number: '1.1.1', - posture_type: 'kspm', - }, - default_value: chance.sentence(), - description: chance.paragraph(), - id: chance.guid(), - impact: chance.word(), - name: chance.string(), - profile_applicability: chance.sentence(), - rationale: chance.paragraph(), - references: chance.paragraph(), - rego_rule_id: 'cis_X_X_X', - remediation: chance.word(), - section: chance.sentence(), - tags: [], - version: '1.0', - }, - agent: { - id: chance.string(), - name: chance.string(), - type: chance.string(), - version: chance.string(), - }, - resource: { - name: chance.string(), - type: chance.string(), - raw: {} as any, - sub_type: chance.string(), - id: chance.string(), - }, - host: {} as any, - ecs: {} as any, - event: {} as EcsEvent, - '@timestamp': new Date().toISOString(), - data_stream: { - dataset: 'cloud_security_posture.findings', - }, -}); diff --git a/x-pack/plugins/cloud_security_posture/public/test/fixtures/navigation_item.ts b/x-pack/plugins/cloud_security_posture/public/test/fixtures/navigation_item.ts deleted file mode 100644 index 1edffe7af2988..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/test/fixtures/navigation_item.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import Chance from 'chance'; -import type { CspPageNavigationItem } from '../../common/navigation/types'; - -type CreateNavigationItemFixtureInput = { chance?: Chance.Chance } & Partial<CspPageNavigationItem>; -export const createPageNavigationItemFixture = ({ - chance = new Chance(), - name = chance.word(), - path = `/${chance.word()}`, - disabled = undefined, - id = 'cloud_security_posture-dashboard', -}: CreateNavigationItemFixtureInput = {}): CspPageNavigationItem => ({ - name, - path, - disabled, - id, -}); diff --git a/x-pack/plugins/cloud_security_posture/server/create_indices/ingest_pipelines.ts b/x-pack/plugins/cloud_security_posture/server/create_indices/ingest_pipelines.ts index 2073f7b146275..7948fd9d90578 100644 --- a/x-pack/plugins/cloud_security_posture/server/create_indices/ingest_pipelines.ts +++ b/x-pack/plugins/cloud_security_posture/server/create_indices/ingest_pipelines.ts @@ -9,7 +9,6 @@ import type { IngestPutPipelineRequest } from '@elastic/elasticsearch/lib/api/ty import { CSP_INGEST_TIMESTAMP_PIPELINE, CSP_LATEST_FINDINGS_INGEST_TIMESTAMP_PIPELINE, - CSP_LATEST_VULNERABILITIES_INGEST_TIMESTAMP_PIPELINE, } from '../../common/constants'; export const scorePipelineIngestConfig: IngestPutPipelineRequest = { @@ -53,24 +52,3 @@ export const latestFindingsPipelineIngestConfig: IngestPutPipelineRequest = { }, ], }; - -export const latestVulnerabilitiesPipelineIngestConfig: IngestPutPipelineRequest = { - id: CSP_LATEST_VULNERABILITIES_INGEST_TIMESTAMP_PIPELINE, - description: 'Pipeline for cloudbeat latest vulnerabilities index', - processors: [ - { - set: { - field: 'event.ingested', - value: '{{_ingest.timestamp}}', - }, - }, - ], - on_failure: [ - { - set: { - field: 'error.message', - value: '{{ _ingest.on_failure_message }}', - }, - }, - ], -}; 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 370c9676099d6..d5db37879554b 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 @@ -180,10 +180,6 @@ export interface KubernetesVersion { metrics: { 'cloudbeat.kubernetes.version': string }; } -export interface PackagePolicyId { - metrics: { 'cloud_security_posture.package_policy.id': string }; -} - export interface LatestDocTimestamp { metrics: { '@timestamp': string }; } 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 index 31b80b880bcc9..63a9201b1f265 100644 --- 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 @@ -45,7 +45,7 @@ export const defineBulkActionCspBenchmarkRulesRoute = (router: CspRouter) => access: 'internal', path: CSP_BENCHMARK_RULES_BULK_ACTION_ROUTE_PATH, options: { - tags: ['access:cloud-security-posture-read'], + tags: ['access:cloud-security-posture-all'], }, }) .addVersion( diff --git a/x-pack/plugins/cloud_security_posture/server/routes/detection_engine/get_detection_engine_alerts_count_by_rule_tags.ts b/x-pack/plugins/cloud_security_posture/server/routes/detection_engine/get_detection_engine_alerts_count_by_rule_tags.ts index 026cf68819ab2..52ff94ad7086c 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/detection_engine/get_detection_engine_alerts_count_by_rule_tags.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/detection_engine/get_detection_engine_alerts_count_by_rule_tags.ts @@ -15,10 +15,6 @@ import { } from '../../../common/constants'; import { CspRouter } from '../../types'; -export interface VulnerabilitiesStatisticsQueryResult { - total: number; -} - const DEFAULT_ALERTS_INDEX = '.alerts-security.alerts-default' as const; export const getDetectionEngineAlertsCountByRuleTags = async ( diff --git a/x-pack/plugins/cloud_security_posture/server/types.ts b/x-pack/plugins/cloud_security_posture/server/types.ts index 82f80cd95f972..1190c0a1c5556 100644 --- a/x-pack/plugins/cloud_security_posture/server/types.ts +++ b/x-pack/plugins/cloud_security_posture/server/types.ts @@ -22,9 +22,6 @@ import type { Logger, SavedObjectsClientContract, IScopedClusterClient, - KibanaResponseFactory, - RequestHandler, - RouteMethod, } from '@kbn/core/server'; import type { AgentService, @@ -90,18 +87,6 @@ export type CspRequestHandlerContext = CustomRequestHandlerContext<{ alerting: AlertingApiRequestHandlerContext; }>; -/** - * Convenience type for request handlers in CSP that includes the CspRequestHandlerContext type - * @internal - */ -export type CspRequestHandler< - P = unknown, - Q = unknown, - B = unknown, - Method extends RouteMethod = any, - ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory -> = RequestHandler<P, Q, B, CspRequestHandlerContext, Method, ResponseFactory>; - /** * Convenience type for routers in Csp that includes the CspRequestHandlerContext type * @internal diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/actions.ts b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/actions.ts index 7424e49099105..87c702087e9af 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/actions.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/actions.ts @@ -147,8 +147,8 @@ export function getActions( available: (item: FieldVisConfig) => { return item.deletable === true; }, - onClick: (item: FieldVisConfig) => { - dataViewEditorRef.current = services.dataViewFieldEditor?.openDeleteModal({ + onClick: async (item: FieldVisConfig) => { + dataViewEditorRef.current = await services.dataViewFieldEditor?.openDeleteModal({ ctx: { dataView }, fieldName: item.fieldName!, onDelete: refreshPage, 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 38759d4d68ea3..0af5f0453ec8b 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 @@ -57,23 +57,32 @@ export const callAgentExecutor: AgentExecutor<true | false> = async ({ const isOpenAI = llmType === 'openai'; const llmClass = getLlmClass(llmType, bedrockChatEnabled); - const llm = new llmClass({ - actionsClient, - connectorId, - llmType, - logger, - // possible client model override, - // let this be undefined otherwise so the connector handles the model - model: request.body.model, - // ensure this is defined because we default to it in the language_models - // This is where the LangSmith logs (Metadata > Invocation Params) are set - temperature: getDefaultArguments(llmType).temperature, - signal: abortSignal, - streaming: isStream, - // prevents the agent from retrying on failure - // failure could be due to bad connector, we should deliver that result to the client asap - maxRetries: 0, - }); + /** + * Creates a new instance of llmClass. + * + * This function ensures that a new llmClass instance is created every time it is called. + * This is necessary to avoid any potential side effects from shared state. By always + * creating a new instance, we prevent other uses of llm from binding and changing + * the state unintentionally. For this reason, never assign this value to a variable (ex const llm = createLlmInstance()) + */ + const createLlmInstance = () => + new llmClass({ + actionsClient, + connectorId, + llmType, + logger, + // possible client model override, + // let this be undefined otherwise so the connector handles the model + model: request.body.model, + // ensure this is defined because we default to it in the language_models + // This is where the LangSmith logs (Metadata > Invocation Params) are set + temperature: getDefaultArguments(llmType).temperature, + signal: abortSignal, + streaming: isStream, + // prevents the agent from retrying on failure + // failure could be due to bad connector, we should deliver that result to the client asap + maxRetries: 0, + }); const anonymizationFieldsRes = await dataClients?.anonymizationFieldsDataClient?.findDocuments<EsAnonymizationFieldsSchema>({ @@ -99,7 +108,7 @@ export const callAgentExecutor: AgentExecutor<true | false> = async ({ const modelExists = await esStore.isModelInstalled(); // 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 chain = RetrievalQAChain.fromLLM(createLlmInstance(), esStore.asRetriever(10)); // Fetch any applicable tools that the source plugin may have registered const assistantToolParams: AssistantToolParams = { @@ -108,7 +117,6 @@ export const callAgentExecutor: AgentExecutor<true | false> = async ({ chain, esClient, isEnabledKnowledgeBase: true, - llm, logger, modelExists, onNewReplacements, @@ -118,7 +126,7 @@ export const callAgentExecutor: AgentExecutor<true | false> = async ({ }; const tools: ToolInterface[] = assistantTools.flatMap( - (tool) => tool.getTool(assistantToolParams) ?? [] + (tool) => tool.getTool({ ...assistantToolParams, llm: createLlmInstance() }) ?? [] ); logger.debug( @@ -132,14 +140,14 @@ export const callAgentExecutor: AgentExecutor<true | false> = async ({ }; // isOpenAI check is not on agentType alone because typescript doesn't like const executor = isOpenAI - ? await initializeAgentExecutorWithOptions(tools, llm, { + ? await initializeAgentExecutorWithOptions(tools, createLlmInstance(), { agentType: 'openai-functions', ...executorArgs, }) : llmType === 'bedrock' && bedrockChatEnabled ? new lcAgentExecutor({ agent: await createToolCallingAgent({ - llm, + llm: createLlmInstance(), tools, prompt: ChatPromptTemplate.fromMessages([ ['system', 'You are a helpful assistant'], @@ -151,7 +159,7 @@ export const callAgentExecutor: AgentExecutor<true | false> = async ({ }), tools, }) - : await initializeAgentExecutorWithOptions(tools, llm, { + : await initializeAgentExecutorWithOptions(tools, createLlmInstance(), { agentType: 'structured-chat-zero-shot-react-description', ...executorArgs, returnIntermediateSteps: false, diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/graph.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/graph.ts index 7ee0e6912b563..6eac3f1c98303 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/graph.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/graph.ts @@ -41,8 +41,7 @@ interface GetDefaultAssistantGraphParams { agentRunnable: AgentRunnableSequence; dataClients?: AssistantDataClients; conversationId?: string; - getLlmInstance: () => BaseChatModel; - llm: BaseChatModel; + createLlmInstance: () => BaseChatModel; logger: Logger; tools: StructuredTool[]; responseLanguage: string; @@ -61,8 +60,7 @@ export const getDefaultAssistantGraph = ({ agentRunnable, conversationId, dataClients, - getLlmInstance, - llm, + createLlmInstance, logger, responseLanguage, tools, @@ -106,7 +104,6 @@ export const getDefaultAssistantGraph = ({ // Default node parameters const nodeParams: NodeParamsBase = { - model: llm, logger, }; @@ -131,6 +128,7 @@ export const getDefaultAssistantGraph = ({ const generateChatTitleNode = (state: AgentState) => generateChatTitle({ ...nodeParams, + model: createLlmInstance(), state, responseLanguage, }); @@ -154,7 +152,7 @@ export const getDefaultAssistantGraph = ({ const respondNode = (state: AgentState) => respond({ ...nodeParams, - llm: getLlmInstance(), + model: createLlmInstance(), state, }); const shouldContinueEdge = (state: AgentState) => shouldContinue({ ...nodeParams, state }); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts index 40336561149e6..758a3a757eb76 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts @@ -57,7 +57,16 @@ export const callAssistantGraph: AgentExecutor<true | false> = async ({ const logger = parentLogger.get('defaultAssistantGraph'); const isOpenAI = llmType === 'openai'; const llmClass = getLlmClass(llmType, bedrockChatEnabled); - const getLlmInstance = () => + + /** + * Creates a new instance of llmClass. + * + * This function ensures that a new llmClass instance is created every time it is called. + * This is necessary to avoid any potential side effects from shared state. By always + * creating a new instance, we prevent other uses of llm from binding and changing + * the state unintentionally. For this reason, never assign this value to a variable (ex const llm = createLlmInstance()) + */ + const createLlmInstance = () => new llmClass({ actionsClient, connectorId, @@ -76,8 +85,6 @@ export const callAssistantGraph: AgentExecutor<true | false> = async ({ maxRetries: 0, }); - const llm = getLlmInstance(); - const anonymizationFieldsRes = await dataClients?.anonymizationFieldsDataClient?.findDocuments<EsAnonymizationFieldsSchema>({ perPage: 1000, @@ -93,7 +100,7 @@ export const callAssistantGraph: AgentExecutor<true | false> = async ({ const modelExists = await esStore.isModelInstalled(); // Create a chain that uses the ELSER backed ElasticsearchStore, override k=10 for esql query generation for now - const chain = RetrievalQAChain.fromLLM(getLlmInstance(), esStore.asRetriever(10)); + const chain = RetrievalQAChain.fromLLM(createLlmInstance(), esStore.asRetriever(10)); // Check if KB is available const isEnabledKnowledgeBase = (await dataClients?.kbDataClient?.isModelDeployed()) ?? false; @@ -106,7 +113,6 @@ export const callAssistantGraph: AgentExecutor<true | false> = async ({ esClient, isEnabledKnowledgeBase, kbDataClient: dataClients?.kbDataClient, - llm, logger, modelExists, onNewReplacements, @@ -116,26 +122,26 @@ export const callAssistantGraph: AgentExecutor<true | false> = async ({ }; const tools: StructuredTool[] = assistantTools.flatMap( - (tool) => tool.getTool(assistantToolParams) ?? [] + (tool) => tool.getTool({ ...assistantToolParams, llm: createLlmInstance() }) ?? [] ); const agentRunnable = isOpenAI ? await createOpenAIFunctionsAgent({ - llm, + llm: createLlmInstance(), tools, prompt: openAIFunctionAgentPrompt, streamRunnable: isStream, }) : llmType && ['bedrock', 'gemini'].includes(llmType) && bedrockChatEnabled ? await createToolCallingAgent({ - llm, + llm: createLlmInstance(), tools, prompt: llmType === 'bedrock' ? bedrockToolCallingAgentPrompt : geminiToolCallingAgentPrompt, streamRunnable: isStream, }) : await createStructuredChatAgent({ - llm, + llm: createLlmInstance(), tools, prompt: structuredChatAgentPrompt, streamRunnable: isStream, @@ -147,9 +153,8 @@ export const callAssistantGraph: AgentExecutor<true | false> = async ({ agentRunnable, conversationId, dataClients, - llm, // we need to pass it like this or streaming does not work for bedrock - getLlmInstance, + createLlmInstance, logger, tools, responseLanguage, diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts index 74cf0fca3929a..9cda33fdbabbc 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts @@ -7,6 +7,7 @@ import { StringOutputParser } from '@langchain/core/output_parsers'; import { ChatPromptTemplate } from '@langchain/core/prompts'; +import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { AgentState, NodeParamsBase } from '../types'; export const GENERATE_CHAT_TITLE_PROMPT = (responseLanguage: string) => @@ -25,6 +26,7 @@ export const GENERATE_CHAT_TITLE_PROMPT = (responseLanguage: string) => export interface GenerateChatTitleParams extends NodeParamsBase { responseLanguage: string; state: AgentState; + model: BaseChatModel; } export const GENERATE_CHAT_TITLE_NODE = 'generateChatTitle'; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/respond.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/respond.ts index bb3b3a518e06d..7c11b96bbca0d 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/respond.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/respond.ts @@ -11,7 +11,7 @@ import { AGENT_NODE_TAG } from './run_agent'; import { AgentState } from '../types'; export const RESPOND_NODE = 'respond'; -export const respond = async ({ llm, state }: { llm: BaseChatModel; state: AgentState }) => { +export const respond = async ({ model, state }: { model: BaseChatModel; state: AgentState }) => { if (state?.agentOutcome && 'returnValues' in state.agentOutcome) { const userMessage = [ 'user', @@ -21,7 +21,7 @@ export const respond = async ({ llm, state }: { llm: BaseChatModel; state: Agent Do not verify, confirm or anything else. Just reply with the same content as provided above.`, ] as [StringWithAutocomplete<'user'>, string]; - const responseMessage = await llm + const responseMessage = await model // use AGENT_NODE_TAG to identify as agent node for stream parsing .withConfig({ runName: 'Summarizer', tags: [AGENT_NODE_TAG] }) .invoke([userMessage]); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/types.ts index 4ee4f1ba1b148..5d86a0f6b97ed 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/types.ts @@ -7,7 +7,6 @@ import { BaseMessage } from '@langchain/core/messages'; import { AgentAction, AgentFinish, AgentStep } from '@langchain/core/agents'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import type { Logger } from '@kbn/logging'; import { ConversationResponse } from '@kbn/elastic-assistant-common'; @@ -25,5 +24,4 @@ export interface AgentState extends AgentStateBase { export interface NodeParamsBase { logger: Logger; - model: BaseChatModel; } diff --git a/x-pack/plugins/enterprise_search/kibana.jsonc b/x-pack/plugins/enterprise_search/kibana.jsonc index 0dc2562ff2fe3..91221ef0ed95e 100644 --- a/x-pack/plugins/enterprise_search/kibana.jsonc +++ b/x-pack/plugins/enterprise_search/kibana.jsonc @@ -15,6 +15,7 @@ "features", "licensing", "logsShared", + "logsDataAccess", "esUiShared", "navigation", ], diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index adfbcb299238b..6509786f2c53e 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -93,6 +93,7 @@ export const agentAssetTypes = { export const dataTypes = { Logs: 'logs', Metrics: 'metrics', + Traces: 'traces', } as const; // currently identical but may be a subset or otherwise different some day diff --git a/x-pack/plugins/fleet/common/services/generate_new_agent_policy.test.ts b/x-pack/plugins/fleet/common/services/generate_new_agent_policy.test.ts index bc4a6b55f75ee..82bda9d6e28ae 100644 --- a/x-pack/plugins/fleet/common/services/generate_new_agent_policy.test.ts +++ b/x-pack/plugins/fleet/common/services/generate_new_agent_policy.test.ts @@ -15,7 +15,7 @@ describe('generateNewAgentPolicyWithDefaults', () => { name: '', description: '', namespace: 'default', - monitoring_enabled: ['logs', 'metrics'], + monitoring_enabled: ['logs', 'metrics', 'traces'], inactivity_timeout: 1209600, is_protected: false, }); diff --git a/x-pack/plugins/fleet/kibana.jsonc b/x-pack/plugins/fleet/kibana.jsonc index 54b4a4928594c..dded2caf4c7e2 100644 --- a/x-pack/plugins/fleet/kibana.jsonc +++ b/x-pack/plugins/fleet/kibana.jsonc @@ -24,7 +24,8 @@ "files", "uiActions", "dashboard", - "fieldsMetadata" + "fieldsMetadata", + "logsDataAccess" ], "optionalPlugins": [ "features", diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index 729bbb3a903ed..ac7d0e9db77fc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -491,7 +491,7 @@ describe('When on the package policy create page', () => { expect(sendCreateAgentPolicy as jest.MockedFunction<any>).toHaveBeenCalledWith( { description: '', - monitoring_enabled: ['logs', 'metrics'], + monitoring_enabled: ['logs', 'metrics', 'traces'], name: 'Agent policy 2', namespace: 'default', inactivity_timeout: 1209600, @@ -526,7 +526,7 @@ describe('When on the package policy create page', () => { expect(sendCreateAgentPolicy as jest.MockedFunction<any>).toHaveBeenCalledWith( { description: '', - monitoring_enabled: ['logs', 'metrics'], + monitoring_enabled: ['logs', 'metrics', 'traces'], name: 'Agent policy 2', namespace: 'default', inactivity_timeout: 1209600, @@ -826,7 +826,7 @@ describe('When on the package policy create page', () => { expect(sendGetOneAgentPolicy).not.toHaveBeenCalled(); expect(sendCreateAgentPolicy).toHaveBeenCalledWith( expect.objectContaining({ - monitoring_enabled: ['logs', 'metrics'], + monitoring_enabled: ['logs', 'metrics', 'traces'], name: 'Agent policy 1', }), { withSysMonitoring: true } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx index 8e52ced483d72..6669f3dfa332f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.test.tsx @@ -541,7 +541,7 @@ describe('edit package policy page', () => { expect(sendCreateAgentPolicy as jest.MockedFunction<any>).toHaveBeenCalledWith( { description: '', - monitoring_enabled: ['logs', 'metrics'], + monitoring_enabled: ['logs', 'metrics', 'traces'], name: 'Agent policy 2', namespace: 'default', inactivity_timeout: 1209600, 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 6f1390f5e17cd..3ae1c4a6cffba 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 @@ -12,7 +12,7 @@ import { appContextService } from '../../..'; import { getPackageSavedObjects } from '../../packages/get'; const INDEX_PATTERN_SAVED_OBJECT_TYPE = 'index-pattern'; -export const indexPatternTypes = Object.values(dataTypes); +export const indexPatternTypes = [dataTypes.Logs, dataTypes.Metrics]; export function getIndexPatternSavedObjects() { return indexPatternTypes.map((indexPatternType) => ({ 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 ae2f5721aae8b..1911ed14a7c80 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -40,7 +40,7 @@ import type { InstallResult } from '../../../common'; import { appContextService } from '..'; -import type { CustomPackageDatasetConfiguration } from './packages/install'; +import type { CustomPackageDatasetConfiguration, EnsurePackageResult } from './packages/install'; import type { FetchFindLatestPackageOptions } from './registry'; import { getPackageFieldsMetadata } from './registry'; @@ -73,7 +73,7 @@ export interface PackageClient { pkgVersion?: string; spaceId?: string; force?: boolean; - }): Promise<Installation>; + }): Promise<EnsurePackageResult>; installPackage(options: { pkgName: string; @@ -201,7 +201,7 @@ class PackageClientImpl implements PackageClient { pkgVersion?: string; spaceId?: string; force?: boolean; - }): Promise<Installation> { + }): Promise<EnsurePackageResult> { await this.#runPreflight(INSTALL_PACKAGES_AUTHZ); return ensureInstalledPackage({ 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 f27cb794475c9..ce406773a0635 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -158,6 +158,11 @@ export async function isPackageVersionOrLaterInstalled(options: { }); } +export interface EnsurePackageResult { + status: InstallResultStatus; + package: Installation; +} + export async function ensureInstalledPackage(options: { savedObjectsClient: SavedObjectsClientContract; pkgName: string; @@ -166,7 +171,7 @@ export async function ensureInstalledPackage(options: { spaceId?: string; force?: boolean; authorizationHeader?: HTTPAuthorizationHeader | null; -}): Promise<Installation> { +}): Promise<EnsurePackageResult> { const { savedObjectsClient, pkgName, @@ -189,7 +194,10 @@ export async function ensureInstalledPackage(options: { }); if (installedPackageResult) { - return installedPackageResult.package; + return { + status: 'already_installed', + package: installedPackageResult.package, + }; } const pkgkey = Registry.pkgToPkgKey(pkgKeyProps); const installResult = await installPackage({ @@ -226,7 +234,10 @@ export async function ensureInstalledPackage(options: { const installation = await getInstallation({ savedObjectsClient, pkgName }); if (!installation) throw new FleetError(`Could not get installation for ${pkgName}`); - return installation; + return { + status: 'installed', + package: installation, + }; } export async function handleInstallPackageFailure({ diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 43928b3737e21..ca5858dccb5c5 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -56,7 +56,11 @@ export const AgentPolicyBaseSchema = { }), monitoring_enabled: schema.maybe( schema.arrayOf( - schema.oneOf([schema.literal(dataTypes.Logs), schema.literal(dataTypes.Metrics)]) + schema.oneOf([ + schema.literal(dataTypes.Logs), + schema.literal(dataTypes.Metrics), + schema.literal(dataTypes.Traces), + ]) ) ), keep_monitoring_alive: schema.maybe(schema.boolean({ defaultValue: false })), diff --git a/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts b/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts index f6c7767b4e2d0..c808123b14a76 100644 --- a/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts +++ b/x-pack/plugins/index_management/public/application/hooks/use_index_errors.ts @@ -45,12 +45,15 @@ export const useIndexErrors = ( if (!model) { return { field, - error: i18n.translate('xpack.idxMgmt.indexOverview.indexErrors.missingModelError', { - defaultMessage: 'Model not found for inference endpoint {inferenceId}', - values: { - inferenceId: field.source.inference_id as string, - }, - }), + error: i18n.translate( + 'xpack.idxMgmt.indexOverview.indexErrors.missingInferenceEndpointError', + { + defaultMessage: 'Inference endpoint {inferenceId} not found', + values: { + inferenceId: field.source.inference_id as string, + }, + } + ), }; } if (isLocalModel(model)) { diff --git a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.test.tsx b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.test.tsx index af21526b7f14d..a137933afed3f 100644 --- a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.test.tsx +++ b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.test.tsx @@ -40,7 +40,7 @@ describe('SampleLogsInput', () => { describe('when uploading a json logs sample', () => { const type = 'application/json'; - describe('when the file is valid', () => { + describe('when the file is valid json', () => { const logsSampleRaw = `{"message":"test message 1"},{"message":"test message 2"}`; beforeEach(async () => { await changeFile(input, new File([`[${logsSampleRaw}]`], 'test.json', { type })); @@ -73,9 +73,11 @@ describe('SampleLogsInput', () => { describe('when the file is invalid', () => { describe.each([ - ['[{"message":"test message 1"}', `The logs sample file has not a valid ${type} format`], + [ + '[{"message":"test message 1"}', + 'Cannot parse the logs sample file as either a JSON or NDJSON file', + ], ['["test message 1"]', 'The logs sample file contains non-object entries'], - ['{"message":"test message 1"}', 'The logs sample file is not an array'], ['[]', 'The logs sample file is empty'], ])('with logs content %s', (logsSample, errorMessage) => { beforeEach(async () => { @@ -98,7 +100,7 @@ describe('SampleLogsInput', () => { describe('when setting a ndjson logs sample', () => { const type = 'application/x-ndjson'; - describe('when the file is valid', () => { + describe('when the file is valid ndjson', () => { const logsSampleRaw = `{"message":"test message 1"}\n{"message":"test message 2"}`; beforeEach(async () => { await changeFile(input, new File([logsSampleRaw], 'test.json', { type })); @@ -131,7 +133,10 @@ describe('SampleLogsInput', () => { describe('when the file is invalid', () => { describe.each([ - ['{"message":"test message 1"]', `The logs sample file has not a valid ${type} format`], + [ + '{"message":"test message 1"}\n{"message": }', + 'Cannot parse the logs sample file as either a JSON or NDJSON file', + ], ['"test message 1"', 'The logs sample file contains non-object entries'], ['', 'The logs sample file is empty'], ])('with logs content %s', (logsSample, errorMessage) => { diff --git a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.tsx b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.tsx index 072669a6bdd1d..cb4f735cc707c 100644 --- a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.tsx +++ b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/sample_logs_input.tsx @@ -19,24 +19,34 @@ const MaxLogsSampleRows = 10; * Parse the logs sample file content (json or ndjson) and return the parsed logs sample */ const parseLogsContent = ( - fileContent: string | undefined, - fileType: string + fileContent: string | undefined ): { error?: string; isTruncated?: boolean; logsSampleParsed?: string[] } => { if (fileContent == null) { return { error: i18n.LOGS_SAMPLE_ERROR.CAN_NOT_READ }; } let parsedContent; try { - if (fileType === 'application/json') { + parsedContent = fileContent + .split('\n') + .filter((line) => line.trim() !== '') + .map((line) => JSON.parse(line)); + + // Special case for files that can be parsed as both JSON and NDJSON: + // for a one-line array [] -> extract its contents + // for a one-line object {} -> do nothing + if ( + Array.isArray(parsedContent) && + parsedContent.length === 1 && + Array.isArray(parsedContent[0]) + ) { + parsedContent = parsedContent[0]; + } + } catch (parseNDJSONError) { + try { parsedContent = JSON.parse(fileContent); - } else if (fileType === 'application/x-ndjson') { - parsedContent = fileContent - .split('\n') - .filter((line) => line.trim() !== '') - .map((line) => JSON.parse(line)); + } catch (parseJSONError) { + return { error: i18n.LOGS_SAMPLE_ERROR.CAN_NOT_PARSE }; } - } catch (_) { - return { error: i18n.LOGS_SAMPLE_ERROR.FORMAT(fileType) }; } if (!Array.isArray(parsedContent)) { @@ -81,10 +91,7 @@ export const SampleLogsInput = React.memo<SampleLogsInputProps>(({ integrationSe const reader = new FileReader(); reader.onload = function (e) { const fileContent = e.target?.result as string | undefined; // We can safely cast to string since we call `readAsText` to load the file. - const { error, isTruncated, logsSampleParsed } = parseLogsContent( - fileContent, - logsSampleFile.type - ); + const { error, isTruncated, logsSampleParsed } = parseLogsContent(fileContent); setIsParsing(false); setSampleFileError(error); if (error) { @@ -137,7 +144,6 @@ export const SampleLogsInput = React.memo<SampleLogsInputProps>(({ integrationSe onChange={onChangeLogsSample} display="large" aria-label="Upload logs sample file" - accept="application/json,application/x-ndjson" isLoading={isParsing} data-test-subj="logsSampleFilePicker" data-loading={isParsing} diff --git a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts index e4cc004e2673b..0af9f803f71fc 100644 --- a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts +++ b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts @@ -126,11 +126,12 @@ export const LOGS_SAMPLE_ERROR = { defaultMessage: 'Failed to read the logs sample file', } ), - FORMAT: (fileType: string) => - i18n.translate('xpack.integrationAssistant.step.dataStream.logsSample.errorFormat', { - values: { fileType }, - defaultMessage: 'The logs sample file has not a valid {fileType} format', - }), + CAN_NOT_PARSE: i18n.translate( + 'xpack.integrationAssistant.step.dataStream.logsSample.errorCanNotParse', + { + defaultMessage: 'Cannot parse the logs sample file as either a JSON or NDJSON file', + } + ), NOT_ARRAY: i18n.translate('xpack.integrationAssistant.step.dataStream.logsSample.errorNotArray', { defaultMessage: 'The logs sample file is not an array', }), diff --git a/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx b/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx index 7271fb8e1a80b..ba047564a1f07 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/datapanel.tsx @@ -339,7 +339,7 @@ export const InnerFormBasedDataPanel = function InnerFormBasedDataPanel({ editPermission ? async (fieldName: string) => { const indexPatternInstance = await dataViews.get(currentIndexPattern?.id); - closeFieldEditor.current = indexPatternFieldEditor.openDeleteModal({ + closeFieldEditor.current = await indexPatternFieldEditor.openDeleteModal({ ctx: { dataView: indexPatternInstance, }, diff --git a/x-pack/plugins/maps/public/lens/choropleth_chart/suggestions_lazy.ts b/x-pack/plugins/maps/public/lens/choropleth_chart/suggestions_lazy.ts index b94d0a25126d0..64ba7ea4af831 100644 --- a/x-pack/plugins/maps/public/lens/choropleth_chart/suggestions_lazy.ts +++ b/x-pack/plugins/maps/public/lens/choropleth_chart/suggestions_lazy.ts @@ -28,7 +28,7 @@ export function getSuggestionsLazy( suggestionRequest: SuggestionRequest<ChoroplethChartState> ): Array<VisualizationSuggestion<ChoroplethChartState>> { if (!promise) { - promise = new Promise((resolve, reject) => { + promise = new Promise((resolve) => { Promise.all([import('./suggestions'), import('../../util')]) .then(async ([{ getSuggestions }, { getEmsFileLayers }]) => { getSuggestionsActual = getSuggestions; @@ -42,7 +42,7 @@ export function getSuggestionsLazy( } resolve(); }) - .catch(reject); + .catch(resolve); }); return []; } diff --git a/x-pack/plugins/ml/common/types/common.ts b/x-pack/plugins/ml/common/types/common.ts index 65801f2667f71..3e0213b8ab959 100644 --- a/x-pack/plugins/ml/common/types/common.ts +++ b/x-pack/plugins/ml/common/types/common.ts @@ -41,6 +41,7 @@ export interface ListingPageUrlState { sortField: string; sortDirection: string; queryText?: string; + showAll?: boolean; } export type AppPageState<T> = { diff --git a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx index b9f17084bd81f..8578be7a57352 100644 --- a/x-pack/plugins/ml/public/application/model_management/model_actions.tsx +++ b/x-pack/plugins/ml/public/application/model_management/model_actions.tsx @@ -15,10 +15,7 @@ import { DEPLOYMENT_STATE, TRAINED_MODEL_TYPE, } from '@kbn/ml-trained-models-utils'; -import { - ELASTIC_MODEL_TAG, - MODEL_STATE, -} from '@kbn/ml-trained-models-utils/src/constants/trained_models'; +import { MODEL_STATE } from '@kbn/ml-trained-models-utils/src/constants/trained_models'; import { getAnalysisType, type DataFrameAnalysisConfigType, @@ -409,10 +406,7 @@ export function useModelActions({ icon: 'download', type: 'icon', isPrimary: true, - available: (item) => - canCreateTrainedModels && - item.tags.includes(ELASTIC_MODEL_TAG) && - item.state === MODEL_STATE.NOT_DOWNLOADED, + available: (item) => canCreateTrainedModels && item.state === MODEL_STATE.NOT_DOWNLOADED, enabled: (item) => !isLoading, onClick: async (item) => { onModelDownloadRequest(item.model_id); 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 79db82034b8d4..1080488a78895 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 @@ -17,13 +17,15 @@ import { EuiFlexGroup, EuiFlexItem, EuiHealth, + EuiIcon, EuiInMemoryTable, EuiLink, - type EuiSearchBarProps, + EuiProgress, EuiSpacer, + EuiSwitch, EuiTitle, EuiToolTip, - EuiProgress, + type EuiSearchBarProps, } from '@elastic/eui'; import { groupBy, isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; @@ -94,6 +96,7 @@ export type ModelItem = TrainedModelConfigResponse & { */ stateDescription?: string; recommended?: boolean; + supported: boolean; /** * Model name, e.g. elser */ @@ -129,6 +132,7 @@ export const getDefaultModelsListState = (): ListingPageUrlState => ({ pageSize: 10, sortField: modelIdColumnName, sortDirection: 'asc', + showAll: false, }); interface Props { @@ -286,9 +290,13 @@ export const ModelsList: FC<Props> = ({ ); const forDownload = await trainedModelsApiService.getTrainedModelDownloads(); const notDownloaded: ModelItem[] = forDownload - .filter(({ model_id: modelId, hidden, recommended }) => { - if (recommended && idMap.has(modelId)) { - idMap.get(modelId)!.recommended = true; + .filter(({ model_id: modelId, hidden, recommended, supported }) => { + if (idMap.has(modelId)) { + const model = idMap.get(modelId)!; + if (recommended) { + model.recommended = true; + } + model.supported = supported; } return !idMap.has(modelId) && !hidden; }) @@ -306,6 +314,7 @@ export const ModelsList: FC<Props> = ({ arch: modelDefinition.arch, softwareLicense: modelDefinition.license, licenseUrl: modelDefinition.licenseUrl, + supported: modelDefinition.supported, } as ModelItem; }); resultItems = [...resultItems, ...notDownloaded]; @@ -530,12 +539,6 @@ export const ModelsList: FC<Props> = ({ try { setIsLoading(true); await trainedModelsApiService.installElasticTrainedModelConfig(modelId); - displaySuccessToast( - i18n.translate('xpack.ml.trainedModels.modelsList.downloadSuccess', { - defaultMessage: '"{modelId}" model download has been started successfully.', - values: { modelId }, - }) - ); // Need to fetch model state updates await fetchModelsData(); } catch (e) { @@ -549,7 +552,7 @@ export const ModelsList: FC<Props> = ({ setIsLoading(true); } }, - [displayErrorToast, displaySuccessToast, fetchModelsData, trainedModelsApiService] + [displayErrorToast, fetchModelsData, trainedModelsApiService] ); /** @@ -633,26 +636,28 @@ export const ModelsList: FC<Props> = ({ }), truncateText: false, 'data-test-subj': 'mlModelsTableColumnDescription', - render: ({ description, recommended }: ModelItem) => { + render: ({ description, recommended, tags, supported }: ModelItem) => { if (!description) return null; const descriptionText = description.replace('(Tech Preview)', ''); - return recommended ? ( - <EuiToolTip - content={ - <FormattedMessage - id="xpack.ml.trainedModels.modelsList.recommendedDownloadContent" - defaultMessage="Recommended model version for your cluster's hardware configuration" - /> - } - > + + const tooltipContent = + supported === false ? ( + <FormattedMessage + id="xpack.ml.trainedModels.modelsList.notSupportedDownloadContent" + defaultMessage="Model version is not supported by your cluster's hardware configuration" + /> + ) : recommended === false ? ( + <FormattedMessage + id="xpack.ml.trainedModels.modelsList.notRecommendedDownloadContent" + defaultMessage="Model version is not optimized for your cluster's hardware configuration" + /> + ) : null; + + return tooltipContent ? ( + <EuiToolTip content={tooltipContent}> <> {descriptionText}  - <b> - <FormattedMessage - id="xpack.ml.trainedModels.modelsList.recommendedDownloadLabel" - defaultMessage="(Recommended)" - /> - </b> + <EuiIcon type={'warning'} color="warning" /> </> </EuiToolTip> ) : ( @@ -861,6 +866,14 @@ export const ModelsList: FC<Props> = ({ const isElserCalloutVisible = !isElserCalloutDismissed && items.findIndex((i) => i.model_id === ELSER_ID_V1) >= 0; + const tableItems = useMemo(() => { + if (pageState.showAll) { + return items; + } else { + return items.filter((item) => item.supported !== false); + } + }, [items, pageState.showAll]); + if (!isInitialized) return null; return ( @@ -868,8 +881,24 @@ export const ModelsList: FC<Props> = ({ <SavedObjectsWarning onCloseFlyout={fetchModelsData} forceRefresh={isLoading} /> <EuiFlexGroup justifyContent="spaceBetween"> {modelsStats ? ( - <EuiFlexItem grow={false}> - <StatsBar stats={modelsStats} dataTestSub={'mlInferenceModelsStatsBar'} /> + <EuiFlexItem> + <EuiFlexGroup alignItems="center"> + <EuiFlexItem grow={false}> + <StatsBar stats={modelsStats} dataTestSub={'mlInferenceModelsStatsBar'} /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiSwitch + label={ + <FormattedMessage + id="xpack.ml.trainedModels.modelsList.showAllLabel" + defaultMessage="Show all" + /> + } + checked={!!pageState.showAll} + onChange={(e) => updatePageState({ showAll: e.target.checked })} + /> + </EuiFlexItem> + </EuiFlexGroup> </EuiFlexItem> ) : null} <EuiFlexItem grow={false}> @@ -894,7 +923,7 @@ export const ModelsList: FC<Props> = ({ allowNeutralSort={false} columns={columns} itemIdToExpandedRowMap={itemIdToExpandedRowMap} - items={items} + items={tableItems} itemId={ModelsTableToConfigMapping.id} loading={isLoading} search={search} 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 85d11ebf983e5..33530bade5fcf 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 @@ -58,6 +58,7 @@ describe('modelsProvider', () => { config: { input: { field_names: ['text_field'] } }, description: 'Elastic Learned Sparse EncodeR v1 (Tech Preview)', hidden: true, + supported: false, model_id: '.elser_model_1', version: 1, modelName: 'elser', @@ -66,6 +67,7 @@ describe('modelsProvider', () => { { config: { input: { field_names: ['text_field'] } }, default: true, + supported: true, description: 'Elastic Learned Sparse EncodeR v2', model_id: '.elser_model_2', version: 2, @@ -79,6 +81,7 @@ describe('modelsProvider', () => { model_id: '.elser_model_2_linux-x86_64', os: 'Linux', recommended: true, + supported: true, version: 2, modelName: 'elser', type: ['elastic', 'pytorch', 'text_expansion'], @@ -88,6 +91,7 @@ describe('modelsProvider', () => { description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', model_id: '.multilingual-e5-small', default: true, + supported: true, version: 1, modelName: 'e5', license: 'MIT', @@ -102,6 +106,7 @@ describe('modelsProvider', () => { model_id: '.multilingual-e5-small_linux-x86_64', os: 'Linux', recommended: true, + supported: true, version: 1, modelName: 'e5', license: 'MIT', @@ -140,6 +145,7 @@ describe('modelsProvider', () => { config: { input: { field_names: ['text_field'] } }, description: 'Elastic Learned Sparse EncodeR v1 (Tech Preview)', hidden: true, + supported: false, model_id: '.elser_model_1', version: 1, modelName: 'elser', @@ -148,6 +154,7 @@ describe('modelsProvider', () => { { config: { input: { field_names: ['text_field'] } }, recommended: true, + supported: true, description: 'Elastic Learned Sparse EncodeR v2', model_id: '.elser_model_2', version: 2, @@ -163,12 +170,14 @@ describe('modelsProvider', () => { version: 2, modelName: 'elser', type: ['elastic', 'pytorch', 'text_expansion'], + supported: false, }, { config: { input: { field_names: ['text_field'] } }, description: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', model_id: '.multilingual-e5-small', recommended: true, + supported: true, version: 1, modelName: 'e5', type: ['pytorch', 'text_embedding'], @@ -182,6 +191,7 @@ describe('modelsProvider', () => { 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', model_id: '.multilingual-e5-small_linux-x86_64', os: 'Linux', + supported: false, version: 1, modelName: 'e5', type: ['pytorch', 'text_embedding'], diff --git a/x-pack/plugins/ml/server/models/model_management/models_provider.ts b/x-pack/plugins/ml/server/models/model_management/models_provider.ts index 6f1cf3c39727c..3345c18ea2b55 100644 --- a/x-pack/plugins/ml/server/models/model_management/models_provider.ts +++ b/x-pack/plugins/ml/server/models/model_management/models_provider.ts @@ -476,6 +476,7 @@ export class ModelsProvider { const modelDefinitionResponse = { ...def, ...(recommended ? { recommended } : {}), + supported: !!def.default || recommended, model_id: modelId, }; diff --git a/x-pack/plugins/observability_solution/apm/common/entities/types.ts b/x-pack/plugins/observability_solution/apm/common/entities/types.ts index f953fb5c593ed..bdca62bc66824 100644 --- a/x-pack/plugins/observability_solution/apm/common/entities/types.ts +++ b/x-pack/plugins/observability_solution/apm/common/entities/types.ts @@ -27,4 +27,5 @@ export interface EntityServiceListItem { environments: string[]; serviceName: string; agentName: AgentName; + hasLogMetrics: boolean; } diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx index c73526a5350c5..49856327dd703 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/add_apm_callout.tsx @@ -16,6 +16,7 @@ import { EuiTitle, EuiButtonEmpty, useEuiTheme, + EuiButtonIcon, } from '@elastic/eui'; import { apmLight } from '@kbn/shared-svg'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -23,7 +24,11 @@ import { useKibana } from '../../../../context/kibana_context/use_kibana'; import { ApmPluginStartDeps, ApmServices } from '../../../../plugin'; import { AddApmData } from '../../../shared/add_data_buttons/buttons'; -export function AddAPMCallOut() { +interface Props { + onClose: () => void; +} + +export function AddAPMCallOut({ onClose }: Props) { const { euiTheme } = useEuiTheme(); const { services } = useKibana<ApmPluginStartDeps & ApmServices>(); @@ -35,41 +40,56 @@ export function AddAPMCallOut() { return ( <EuiPanel color="subdued" hasShadow={false}> - <EuiFlexGroup alignItems="center" gutterSize="s" justifyContent="flexStart"> - <EuiFlexItem grow={0}> - <EuiImage - css={{ - background: euiTheme.colors.emptyShade, - }} - width="160" - height="100" - size="m" - src={apmLight} - alt="apm-logo" - /> - </EuiFlexItem> - <EuiFlexItem grow={4}> - <EuiTitle size="xs"> - <h1> - <FormattedMessage - id="xpack.apm.addAPMCallOut.title" - defaultMessage="Detect and resolve issues faster with deep visibility into your application" + <EuiFlexGroup gutterSize="s"> + <EuiFlexItem> + <EuiFlexGroup alignItems="center" gutterSize="s" justifyContent="flexStart"> + <EuiFlexItem grow={0}> + <EuiImage + css={{ + background: euiTheme.colors.emptyShade, + }} + width="160" + height="100" + size="m" + src={apmLight} + alt="apm-logo" /> - </h1> - </EuiTitle> + </EuiFlexItem> + <EuiFlexItem grow={4}> + <EuiTitle size="xs"> + <h1> + <FormattedMessage + id="xpack.apm.addAPMCallOut.title" + defaultMessage="Detect and resolve issues faster with deep visibility into your application" + /> + </h1> + </EuiTitle> - <EuiSpacer size="m" /> + <EuiSpacer size="m" /> - <EuiText size="s"> - <p> - <FormattedMessage - id="xpack.apm.addAPMCallOut.description" - defaultMessage="Understanding your application performance, relationships and dependencies by - instrumenting with APM." + <EuiText size="s"> + <p> + <FormattedMessage + id="xpack.apm.addAPMCallOut.description" + defaultMessage="Understanding your application performance, relationships and dependencies by + instrumenting with APM." + /> + </p> + </EuiText> + <EuiSpacer size="s" /> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiFlexGroup> + <EuiFlexItem grow={false}> + <EuiButtonIcon + data-test-subj="apmAddAPMCallOutButton" + iconType="cross" + onClick={onClose} /> - </p> - </EuiText> - <EuiSpacer size="s" /> + </EuiFlexItem> + </EuiFlexGroup> </EuiFlexItem> </EuiFlexGroup> <EuiFlexGroup alignItems="center" gutterSize="s" justifyContent="flexEnd"> diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx index d1f08b16eaf15..c89487b527fbe 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/entities/logs/logs_service_overview.tsx @@ -12,17 +12,29 @@ * 2.0. */ +import { + EuiCallOut, + EuiFlexGroup, + EuiFlexGroupProps, + EuiFlexItem, + EuiLink, + EuiLoadingSpinner, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { EuiFlexGroupProps, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { AnnotationsContextProvider } from '../../../../context/annotations/annotations_context'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { ChartPointerEventContextProvider } from '../../../../context/chart_pointer_event/chart_pointer_event_context'; -import { useBreakpoints } from '../../../../hooks/use_breakpoints'; import { useApmParams } from '../../../../hooks/use_apm_params'; +import { useBreakpoints } from '../../../../hooks/use_breakpoints'; import { useTimeRange } from '../../../../hooks/use_time_range'; -import { AddAPMCallOut } from './add_apm_callout'; -import { LogRateChart } from '../charts/log_rate_chart'; import { LogErrorRateChart } from '../charts/log_error_rate_chart'; +import { LogRateChart } from '../charts/log_rate_chart'; +import { AddAPMCallOut } from './add_apm_callout'; +import { useLocalStorage } from '../../../../hooks/use_local_storage'; +import { isPending, useFetcher } from '../../../../hooks/use_fetcher'; /** * The height a chart should be if it's next to a table with 5 rows and a title. * Add the height of the pagination row. @@ -32,6 +44,10 @@ const chartHeight = 400; export function LogsServiceOverview() { const { serviceName } = useApmServiceContext(); + const [isLogsApmCalloutEnabled, setIsLogsApmCalloutEnabled] = useLocalStorage( + 'apm.isLogsApmCalloutEnabled', + true + ); const { query: { environment, rangeFrom, rangeTo }, @@ -39,11 +55,28 @@ export function LogsServiceOverview() { const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const { data, status } = useFetcher( + (callAPI) => { + return callAPI('GET /internal/apm/entities/services/{serviceName}/summary', { + params: { path: { serviceName }, query: { end, environment, start } }, + }); + }, + [end, environment, serviceName, start] + ); + const { isLarge } = useBreakpoints(); const isSingleColumn = isLarge; const rowDirection: EuiFlexGroupProps['direction'] = isSingleColumn ? 'column' : 'row'; + if (isPending(status)) { + return ( + <div style={{ textAlign: 'center' }}> + <EuiLoadingSpinner size="xl" /> + </div> + ); + } + return ( <AnnotationsContextProvider serviceName={serviceName} @@ -52,8 +85,60 @@ export function LogsServiceOverview() { end={end} > <ChartPointerEventContextProvider> - <AddAPMCallOut /> - <EuiSpacer size="l" /> + {isLogsApmCalloutEnabled ? ( + <> + <AddAPMCallOut + onClose={() => { + setIsLogsApmCalloutEnabled(false); + }} + /> + <EuiSpacer size="l" /> + </> + ) : null} + {data?.entity?.hasLogMetrics === false ? ( + <> + <EuiCallOut + title={i18n.translate( + 'xpack.apm.logsServiceOverview.euiCallOut.noLogMetricsHaveLabel', + { + defaultMessage: 'No log metrics have been detected against this service', + } + )} + color="warning" + iconType="warning" + > + <FormattedMessage + id="xpack.apm.logsServiceOverview.pleaseEnsureYouAreCallOutLabel" + defaultMessage="Please ensure you are surfacing {logLevelLink} in your logs to display log metrics. {learnMoreLink}" + values={{ + logLevelLink: ( + <EuiLink + data-test-subj="apmNotAvailableLogsMetricsLink" + href="https://www.elastic.co/guide/en/ecs/current/ecs-log.html#field-log-level" + target="_blank" + > + {i18n.translate('xpack.apm.logsServiceOverview.logLevelLink', { + defaultMessage: 'log.level', + })} + </EuiLink> + ), + learnMoreLink: ( + <EuiLink + data-test-subj="apmNotAvailableLogsMetricsLink" + href="https://ela.st/service-logs-level" + target="_blank" + > + {i18n.translate('xpack.apm.logsServiceOverview.learnMoreLink', { + defaultMessage: 'Learn more', + })} + </EuiLink> + ), + }} + /> + </EuiCallOut> + <EuiSpacer size="l" /> + </> + ) : null} <EuiFlexGroup direction="column" gutterSize="s"> <EuiFlexItem> diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx index d0a2cfa898b79..45e8a9ca859a0 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/service_inventory/multi_signal_inventory/table/get_service_columns.tsx @@ -32,13 +32,14 @@ import { EnvironmentBadge } from '../../../../shared/environment_badge'; import { ServiceLink } from '../../../../shared/links/apm/service_link'; import { ListMetric } from '../../../../shared/list_metric'; import { ITableColumn } from '../../../../shared/managed_table'; -import { NotAvailableApmMetrics } from '../../../../shared/not_available_apm_metrics'; +import { NotAvailableApmMetrics } from '../../../../shared/not_available_popover/not_available_apm_metrics'; import { TruncateWithTooltip } from '../../../../shared/truncate_with_tooltip'; import { ServiceInventoryFieldName } from './multi_signal_services_table'; import { EntityServiceListItem, SignalTypes } from '../../../../../../common/entities/types'; -import { isApmSignal } from '../../../../../utils/get_signal_type'; +import { isApmSignal, isLogsSignal } from '../../../../../utils/get_signal_type'; import { ColumnHeader } from './column_header'; import { APIReturnType } from '../../../../../services/rest/create_call_apm_api'; +import { NotAvailableLogsMetrics } from '../../../../shared/not_available_popover/not_available_log_metrics'; type ServicesDetailedStatisticsAPIResponse = APIReturnType<'POST /internal/apm/entities/services/detailed_statistics'>; @@ -205,9 +206,12 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics, serviceName }) => { - const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_RATE); + render: (_, { metrics, serviceName, signalTypes, hasLogMetrics }) => { + if (isLogsSignal(signalTypes) && !hasLogMetrics) { + return <NotAvailableLogsMetrics />; + } + const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_RATE); return ( <ListMetric isLoading={timeseriesDataLoading} @@ -254,7 +258,11 @@ export function getServiceColumns({ sortable: true, dataType: 'number', align: RIGHT_ALIGNMENT, - render: (_, { metrics, serviceName }) => { + render: (_, { metrics, serviceName, signalTypes, hasLogMetrics }) => { + if (isLogsSignal(signalTypes) && !hasLogMetrics) { + return <NotAvailableLogsMetrics />; + } + const { currentPeriodColor } = getTimeSeriesColor(ChartType.LOG_ERROR_RATE); return ( diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx index 135848bae13d4..8e3752e5ce0bf 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/environment_badge/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { ItemsBadge } from '../item_badge'; -import { NotAvailableEnvironment } from '../not_available_environment'; +import { NotAvailableEnvironment } from '../not_available_popover/not_available_environment'; interface Props { environments: string[]; diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_apm_metrics.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_apm_metrics.tsx similarity index 81% rename from x-pack/plugins/observability_solution/apm/public/components/shared/not_available_apm_metrics.tsx rename to x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_apm_metrics.tsx index c4d2eb8563af6..66c466502bdd1 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_apm_metrics.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_apm_metrics.tsx @@ -7,10 +7,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { PopoverBadge } from './popover_badge'; -import { useKibana } from '../../context/kibana_context/use_kibana'; -import { ApmPluginStartDeps, ApmServices } from '../../plugin'; -import { AddApmData } from './add_data_buttons/buttons'; +import { PopoverBadge } from '../popover_badge'; +import { useKibana } from '../../../context/kibana_context/use_kibana'; +import { ApmPluginStartDeps, ApmServices } from '../../../plugin'; +import { AddApmData } from '../add_data_buttons/buttons'; export function NotAvailableApmMetrics() { const { services } = useKibana<ApmPluginStartDeps & ApmServices>(); diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_environment.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_environment.tsx similarity index 92% rename from x-pack/plugins/observability_solution/apm/public/components/shared/not_available_environment.tsx rename to x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_environment.tsx index aecc9f2586e71..af4cdd58ec1f9 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_environment.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_environment.tsx @@ -9,9 +9,9 @@ import React from 'react'; import { EuiCode, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { PopoverBadge } from './popover_badge'; +import { PopoverBadge } from '../popover_badge'; -export const NotAvailableEnvironment = () => { +export function NotAvailableEnvironment() { return ( <PopoverBadge title={i18n.translate('xpack.apm.servicesTable.notAvailableEnv.title', { @@ -39,4 +39,4 @@ export const NotAvailableEnvironment = () => { } /> ); -}; +} diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.tsx new file mode 100644 index 0000000000000..480795c533944 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/not_available_popover/not_available_log_metrics.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 { EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; +import { PopoverBadge } from '../popover_badge'; + +export function NotAvailableLogsMetrics() { + return ( + <PopoverBadge + title={i18n.translate('xpack.apm.servicesTable.notAvailableLogsMetrics.title', { + defaultMessage: 'Want to see more?', + })} + content={ + <FormattedMessage + id="xpack.apm.servicesTable.notAvailableLogsMetrics.content" + defaultMessage="In order to see log metrics against this service, please declare {logLevelLink} in your logs." + values={{ + logLevelLink: ( + <EuiLink + data-test-subj="apmNotAvailableLogsMetricsLink" + href="https://www.elastic.co/guide/en/ecs/current/ecs-log.html#field-log-level" + target="_blank" + > + {i18n.translate( + 'xpack.apm.servicesTable.notAvailableLogsMetrics.content.logLevelLink', + { defaultMessage: 'log.level' } + )} + </EuiLink> + ), + }} + /> + } + footer={ + <EuiLink + data-test-subj="apmNotAvailableLogsMetricsLink" + href="https://ela.st/service-logs-level" + target="_blank" + > + {i18n.translate('xpack.apm.servicesTable.notAvailableLogsMetrics.footer.learnMore', { + defaultMessage: 'Learn more', + })} + </EuiLink> + } + /> + ); +} diff --git a/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts b/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts index a72bb4d782e9c..e8a3c4c4313df 100644 --- a/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts +++ b/x-pack/plugins/observability_solution/apm/public/utils/get_signal_type.ts @@ -11,5 +11,5 @@ export function isApmSignal(signalTypes: SignalTypes[]) { return signalTypes.includes(SignalTypes.METRICS) || signalTypes.includes(SignalTypes.TRACES); } export function isLogsSignal(signalTypes: SignalTypes[]) { - return signalTypes.includes(SignalTypes.LOGS) && !isApmSignal(signalTypes); + return signalTypes.includes(SignalTypes.LOGS); } diff --git a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts index 17193a6d508b6..4ddd64fd0a3bc 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients.ts @@ -13,9 +13,10 @@ import { MsearchMultisearchHeader, } from '@elastic/elasticsearch/lib/api/types'; import { withApmSpan } from '../../../../utils/with_apm_span'; +import { EntityType } from '../../../../routes/entities/types'; -const ENTITIES_LATEST_INDEX_NAME = '.entities.v1.latest.builtin_services*'; -const ENTITIES_HISTORY_INDEX_NAME = '.entities.v1.history.builtin_services*'; +const ENTITIES_LATEST_INDEX_NAME = `entities-${EntityType.SERVICE}-latest`; +const ENTITIES_HISTORY_INDEX_NAME = `entities-${EntityType.SERVICE}-history`; export function cancelEsRequestOnAbort<T extends Promise<any>>( promise: T, @@ -60,7 +61,7 @@ export async function createEntitiesESClient({ const promise = withApmSpan(operationName, () => { return cancelEsRequestOnAbort( esClient.search( - { ...searchRequest, index: [indexName] }, + { ...searchRequest, index: [indexName], ignore_unavailable: true }, { signal: controller.signal, meta: true, @@ -99,6 +100,7 @@ export async function createEntitiesESClient({ const searchParams: [MsearchMultisearchHeader, MsearchMultisearchBody] = [ { index: [ENTITIES_LATEST_INDEX_NAME], + ignore_unavailable: true, }, { ...params.body, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts index 40704fd3fed9f..a69a906454ff7 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_entities.ts @@ -17,6 +17,7 @@ import { environmentQuery } from '../../../common/utils/environment_query'; import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients'; import { getServiceEntitiesHistoryMetrics } from './get_service_entities_history_metrics'; import { EntitiesRaw, EntityType, ServiceEntities } from './types'; +import { isFiniteNumber } from '../../../common/utils/is_finite_number'; export function entitiesRangeQuery(start: number, end: number): QueryDslQueryContainer[] { return [ @@ -44,14 +45,16 @@ export async function getEntities({ environment, kuery, size, + serviceName, }: { entitiesESClient: EntitiesESClient; start: number; end: number; environment: string; - kuery: string; + kuery?: string; size: number; -}) { + serviceName?: string; +}): Promise<ServiceEntities[]> { const entities = ( await entitiesESClient.searchLatest(`get_entities`, { body: { @@ -65,6 +68,7 @@ export async function getEntities({ ...environmentQuery(environment, SERVICE_ENVIRONMENT), ...entitiesRangeQuery(start, end), ...termQuery(ENTITY_TYPE, EntityType.SERVICE), + ...termQuery(SERVICE_NAME, serviceName), ], }, }, @@ -82,7 +86,8 @@ export async function getEntities({ }) : undefined; - return entities.map((entity): ServiceEntities => { + return entities.map((entity) => { + const historyLogRate = serviceEntitiesHistoryMetricsMap?.[entity.entity.id]?.logRate; return { serviceName: entity.service.name, environment: Array.isArray(entity.service?.environment) // TODO fix this in the EEM @@ -92,6 +97,7 @@ export async function getEntities({ signalTypes: entity.data_stream.type, entity: { ...entity.entity, + hasLogMetrics: isFiniteNumber(historyLogRate) ? historyLogRate > 0 : false, // History metrics undefined means that for the selected time range there was no ingestion happening. metrics: serviceEntitiesHistoryMetricsMap?.[entity.entity.id] || { latency: null, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/get_service_entity_summary.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_service_entity_summary.ts new file mode 100644 index 0000000000000..0ea5d27e68971 --- /dev/null +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/get_service_entity_summary.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_assets_es_client/create_assets_es_clients'; +import { withApmSpan } from '../../utils/with_apm_span'; +import { getEntities } from './get_entities'; +import { ServiceEntities } from './types'; + +interface Params { + entitiesESClient: EntitiesESClient; + serviceName: string; + environment: string; + start: number; + end: number; +} + +export async function getServiceEntitySummary({ + end, + entitiesESClient, + environment, + serviceName, + start, +}: Params): Promise<ServiceEntities> { + return withApmSpan('get_service_entity_summary', async () => { + const entities = await getEntities({ + end, + entitiesESClient, + environment, + size: 1, + start, + serviceName, + }); + + return entities[0]; + }); +} diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts index c3894a38da2c4..0f52a9956af5b 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/services/routes.ts @@ -13,12 +13,43 @@ import { createEntitiesESClient } from '../../../lib/helpers/create_es_client/cr import { createApmServerRoute } from '../../apm_routes/create_apm_server_route'; import { environmentRt, kueryRt, rangeRt } from '../../default_api_types'; import { getServiceEntities } from './get_service_entities'; +import { getServiceEntitySummary } from '../get_service_entity_summary'; +import { ServiceEntities } from '../types'; import { getServiceEntitiesHistoryTimeseries } from '../get_service_entities_history_timeseries'; export interface EntityServicesResponse { services: EntityServiceListItem[]; } +const serviceEntitiesSummaryRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/entities/services/{serviceName}/summary', + params: t.type({ + path: t.type({ serviceName: t.string }), + query: t.intersection([environmentRt, rangeRt]), + }), + options: { tags: ['access:apm'] }, + async handler(resources): Promise<ServiceEntities> { + const { context, params, request } = resources; + const coreContext = await context.core; + + const entitiesESClient = await createEntitiesESClient({ + request, + esClient: coreContext.elasticsearch.client.asCurrentUser, + }); + + const { serviceName } = params.path; + const { start, end, environment } = params.query; + + return getServiceEntitySummary({ + entitiesESClient, + start, + end, + serviceName, + environment, + }); + }, +}); + const servicesEntitiesRoute = createApmServerRoute({ endpoint: 'GET /internal/apm/entities/services', params: t.type({ @@ -161,4 +192,5 @@ export const servicesEntitiesRoutesRepository = { ...serviceLogRateTimeseriesRoute, ...serviceLogErrorRateTimeseriesRoute, ...servicesEntitiesDetailedStatisticsRoute, + ...serviceEntitiesSummaryRoute, }; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts index 35a19a218eea0..f98cef107a3d1 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/types.ts @@ -16,6 +16,7 @@ export interface Entity { latestTimestamp: string; identityFields: string[]; metrics: EntityMetrics; + hasLogMetrics: boolean; } export interface TraceMetrics { @@ -52,4 +53,5 @@ export interface MergedServiceEntities { signalTypes: SignalTypes[]; environments: string[]; metrics: EntityMetrics[]; + hasLogMetrics: boolean; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts index a7d18a93bd854..f73d8cd5f4f8d 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/calculate_avg_metrics.test.ts @@ -34,6 +34,7 @@ describe('calculateAverageMetrics', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, { agentName: 'java' as AgentName, @@ -57,6 +58,7 @@ describe('calculateAverageMetrics', () => { }, ], serviceName: 'service-2', + hasLogMetrics: true, }, ]; @@ -76,6 +78,7 @@ describe('calculateAverageMetrics', () => { throughput: 7.5, }, serviceName: 'service-1', + hasLogMetrics: true, }, { agentName: 'java' as AgentName, @@ -90,6 +93,7 @@ describe('calculateAverageMetrics', () => { throughput: 10, }, serviceName: 'service-2', + hasLogMetrics: true, }, ]); }); @@ -117,6 +121,7 @@ describe('calculateAverageMetrics', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]; @@ -135,6 +140,7 @@ describe('calculateAverageMetrics', () => { throughput: 7.5, }, serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts index 544513fd501d2..dc52a8369c1cc 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.test.ts @@ -27,6 +27,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -47,6 +48,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); @@ -69,6 +71,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:env-service-1', + hasLogMetrics: true, }, }, { @@ -87,6 +90,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'apm-only-1:synthtrace-env-2', + hasLogMetrics: true, }, }, { @@ -105,6 +109,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-2:env-service-3', + hasLogMetrics: true, }, }, { @@ -123,6 +128,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-2:env-service-3', + hasLogMetrics: true, }, }, ]; @@ -151,6 +157,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, { agentName: 'java' as AgentName, @@ -174,6 +181,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-2', + hasLogMetrics: true, }, ]); }); @@ -195,6 +203,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -213,6 +222,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -231,6 +241,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name', 'service.environment'], id: 'service-1:prod', + hasLogMetrics: true, }, }, ]; @@ -265,6 +276,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); @@ -286,6 +298,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -306,6 +319,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); @@ -325,6 +339,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -342,6 +357,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -369,6 +385,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); @@ -390,6 +407,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -410,6 +428,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); @@ -429,6 +448,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, { @@ -446,6 +466,7 @@ describe('mergeEntities', () => { }, identityFields: ['service.name'], id: 'service-1:test', + hasLogMetrics: true, }, }, ]; @@ -473,6 +494,7 @@ describe('mergeEntities', () => { }, ], serviceName: 'service-1', + hasLogMetrics: true, }, ]); }); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts index 7dd8bfdace7bf..6f96a25c63a93 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/entities/utils/merge_entities.ts @@ -36,6 +36,7 @@ function mergeFunc(entity: ServiceEntities, existingEntity?: MergedServiceEntiti environments: compact([entity?.environment]), latestTimestamp: entity.entity.latestTimestamp, metrics: [entity.entity.metrics], + hasLogMetrics: entity.entity.hasLogMetrics, }; } return { @@ -45,5 +46,6 @@ function mergeFunc(entity: ServiceEntities, existingEntity?: MergedServiceEntiti environments: uniq(compact([...existingEntity?.environments, entity?.environment])), latestTimestamp: entity.entity.latestTimestamp, metrics: [...existingEntity?.metrics, entity.entity.metrics], + hasLogMetrics: entity.entity.hasLogMetrics || existingEntity.hasLogMetrics, }; } diff --git a/x-pack/plugins/observability_solution/apm_data_access/server/services/get_host_names/index.ts b/x-pack/plugins/observability_solution/apm_data_access/server/services/get_host_names/index.ts new file mode 100644 index 0000000000000..10507a0803d9a --- /dev/null +++ b/x-pack/plugins/observability_solution/apm_data_access/server/services/get_host_names/index.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { estypes } from '@elastic/elasticsearch'; +import { rangeQuery } from '@kbn/observability-plugin/server'; +import { HOST_NAME } from '@kbn/apm-types/es_fields'; +import { getBucketSize, type TimeRangeMetadata } from '../../../common'; +import { getPreferredBucketSizeAndDataSource } from '../../../common/utils/get_preferred_bucket_size_and_data_source'; +import { ApmDocumentType } from '../../../common/document_type'; +import type { ApmDataAccessServicesParams } from '../get_services'; + +const MAX_SIZE = 1000; + +export interface HostNamesRequest { + query: estypes.QueryDslQueryContainer; + kuery?: string; + start: number; + end: number; + size?: number; + documentSources: TimeRangeMetadata['sources']; +} + +const suitableTypes = [ApmDocumentType.TransactionMetric]; + +export function createGetHostNames({ apmEventClient }: ApmDataAccessServicesParams) { + return async ({ start, end, size = MAX_SIZE, query, documentSources }: HostNamesRequest) => { + const sourcesToUse = getPreferredBucketSizeAndDataSource({ + sources: documentSources.filter((s) => suitableTypes.includes(s.documentType)), + bucketSizeInSeconds: getBucketSize({ start, end, numBuckets: 50 }).bucketSize, + }); + + const esResponse = await apmEventClient.search('get_apm_host_names', { + apm: { + sources: [ + { + documentType: sourcesToUse.source.documentType, + rollupInterval: sourcesToUse.source.rollupInterval, + }, + ], + }, + body: { + track_total_hits: false, + size: 0, + query: { + bool: { + filter: [query, ...rangeQuery(start, end)], + }, + }, + aggs: { + hostNames: { + terms: { + field: HOST_NAME, + size: Math.min(size, MAX_SIZE), + }, + }, + }, + }, + }); + + return esResponse.aggregations?.hostNames.buckets.map((bucket) => bucket.key as string) ?? []; + }; +} diff --git a/x-pack/plugins/observability_solution/apm_data_access/server/services/get_services.ts b/x-pack/plugins/observability_solution/apm_data_access/server/services/get_services.ts index edcea39884d93..1d72a1ae8c009 100644 --- a/x-pack/plugins/observability_solution/apm_data_access/server/services/get_services.ts +++ b/x-pack/plugins/observability_solution/apm_data_access/server/services/get_services.ts @@ -7,6 +7,7 @@ import { APMEventClient } from '../lib/helpers/create_es_client/create_apm_event_client'; import { createGetDocumentSources } from './get_document_sources'; +import { createGetHostNames } from './get_host_names'; export interface ApmDataAccessServicesParams { apmEventClient: APMEventClient; @@ -15,5 +16,6 @@ export interface ApmDataAccessServicesParams { export function getServices(params: ApmDataAccessServicesParams) { return { getDocumentSources: createGetDocumentSources(params), + getHostNames: createGetHostNames(params), }; } diff --git a/x-pack/plugins/observability_solution/apm_data_access/server/types.ts b/x-pack/plugins/observability_solution/apm_data_access/server/types.ts index c8f9b38a83874..c6c3d316a9685 100644 --- a/x-pack/plugins/observability_solution/apm_data_access/server/types.ts +++ b/x-pack/plugins/observability_solution/apm_data_access/server/types.ts @@ -21,6 +21,7 @@ export interface ApmDataAccessPluginStart {} export type ApmDataAccessServices = ReturnType<typeof getServices>; export type { ApmDataAccessServicesParams } from './services/get_services'; export type { DocumentSourcesRequest } from './services/get_document_sources'; +export type { HostNamesRequest } from './services/get_host_names'; export type { APMEventClientConfig, APMEventESSearchRequest, diff --git a/x-pack/plugins/observability_solution/assets_data_access/server/plugin.ts b/x-pack/plugins/observability_solution/assets_data_access/server/plugin.ts deleted file mode 100644 index 7de93968ec3ab..0000000000000 --- a/x-pack/plugins/observability_solution/assets_data_access/server/plugin.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { - CoreSetup, - CoreStart, - Logger, - Plugin, - PluginInitializerContext, -} from '@kbn/core/server'; -import { registerServices } from './services/register_services'; -import { AssetsPluginStartDeps } from './types'; - -export type AssetsDataAccessPluginSetup = ReturnType<AssetsDataAccessPlugin['setup']>; -export type AssetsDataAccessPluginStart = ReturnType<AssetsDataAccessPlugin['start']>; - -export class AssetsDataAccessPlugin implements Plugin { - private readonly logger: Logger; - - constructor(initializerContext: PluginInitializerContext) { - this.logger = initializerContext.logger.get(); - } - public setup(core: CoreSetup) {} - - public start(core: CoreStart, plugins: AssetsPluginStartDeps) { - const services = registerServices({ - logger: this.logger, - deps: {}, - }); - - return { services }; - } -} diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/components/dataset_quality/summary_panel/datasets_quality_indicators.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/components/dataset_quality/summary_panel/datasets_quality_indicators.tsx index 836580565b2d8..b186c16c0c0f8 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/components/dataset_quality/summary_panel/datasets_quality_indicators.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/components/dataset_quality/summary_panel/datasets_quality_indicators.tsx @@ -33,13 +33,24 @@ import { mapPercentagesToQualityCounts } from '../../quality_indicator'; export function DatasetsQualityIndicators() { const { onPageReady } = usePerformanceContext(); - const { datasetsQuality, isDatasetsQualityLoading, datasetsActivity } = useSummaryPanelContext(); + const { + datasetsQuality, + isDatasetsQualityLoading, + datasetsActivity, + numberOfDatasets, + numberOfDocuments, + } = useSummaryPanelContext(); const qualityCounts = mapPercentagesToQualityCounts(datasetsQuality.percentages); const datasetsWithoutIgnoredField = datasetsActivity.total > 0 ? datasetsActivity.total - datasetsQuality.percentages.length : 0; - if (!isDatasetsQualityLoading) { - onPageReady(); + if (!isDatasetsQualityLoading && (numberOfDatasets || numberOfDocuments)) { + onPageReady({ + key1: 'datasets', + value1: numberOfDatasets, + key2: 'documents', + value2: numberOfDocuments, + }); } return ( diff --git a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_summary_panel.tsx b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_summary_panel.tsx index a000115c82284..622f495fc4774 100644 --- a/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_summary_panel.tsx +++ b/x-pack/plugins/observability_solution/dataset_quality/public/hooks/use_summary_panel.tsx @@ -85,6 +85,9 @@ const useSummaryPanel = () => { isDatasetsActivityLoading, datasetsActivity, + + numberOfDatasets: filteredItems.length, + numberOfDocuments: filteredItems.reduce((acc, curr) => acc + curr.degradedDocs.docsCount, 0), }; }; diff --git a/x-pack/plugins/observability_solution/entities_data_access/README.md b/x-pack/plugins/observability_solution/entities_data_access/README.md new file mode 100644 index 0000000000000..fdcbce8d19a68 --- /dev/null +++ b/x-pack/plugins/observability_solution/entities_data_access/README.md @@ -0,0 +1,3 @@ +# Entities data access + +Exposes services to access entities data. diff --git a/x-pack/plugins/observability_solution/assets_data_access/jest.config.js b/x-pack/plugins/observability_solution/entities_data_access/jest.config.js similarity index 82% rename from x-pack/plugins/observability_solution/assets_data_access/jest.config.js rename to x-pack/plugins/observability_solution/entities_data_access/jest.config.js index 3c145b378762f..9a813d5ebfee0 100644 --- a/x-pack/plugins/observability_solution/assets_data_access/jest.config.js +++ b/x-pack/plugins/observability_solution/entities_data_access/jest.config.js @@ -10,5 +10,5 @@ const path = require('path'); module.exports = { preset: '@kbn/test', rootDir: path.resolve(__dirname, '../../../..'), - roots: ['<rootDir>/x-pack/plugins/observability_solution/assets_data_access'], + roots: ['<rootDir>/x-pack/plugins/observability_solution/entities_data_access'], }; diff --git a/x-pack/plugins/observability_solution/assets_data_access/kibana.jsonc b/x-pack/plugins/observability_solution/entities_data_access/kibana.jsonc similarity index 58% rename from x-pack/plugins/observability_solution/assets_data_access/kibana.jsonc rename to x-pack/plugins/observability_solution/entities_data_access/kibana.jsonc index b5c33ee7e9e90..78c2e5db5c2b0 100644 --- a/x-pack/plugins/observability_solution/assets_data_access/kibana.jsonc +++ b/x-pack/plugins/observability_solution/entities_data_access/kibana.jsonc @@ -1,9 +1,9 @@ { "type": "plugin", - "id": "@kbn/assets-data-access-plugin", - "owner": "@elastic/obs-knowledge-team", + "id": "@kbn/entities-data-access-plugin", + "owner": "@elastic/obs-entities", "plugin": { - "id": "assetsDataAccess", + "id": "entitiesDataAccess", "server": true, "browser": false, "requiredPlugins": [], diff --git a/x-pack/plugins/observability_solution/assets_data_access/server/index.ts b/x-pack/plugins/observability_solution/entities_data_access/server/index.ts similarity index 57% rename from x-pack/plugins/observability_solution/assets_data_access/server/index.ts rename to x-pack/plugins/observability_solution/entities_data_access/server/index.ts index cca2f9d457638..282f8d7031338 100644 --- a/x-pack/plugins/observability_solution/assets_data_access/server/index.ts +++ b/x-pack/plugins/observability_solution/entities_data_access/server/index.ts @@ -6,11 +6,11 @@ */ import type { PluginInitializerContext } from '@kbn/core/server'; -import type { AssetsDataAccessPluginSetup, AssetsDataAccessPluginStart } from './plugin'; +import type { EntitiesDataAccessPluginSetup, EntitiesDataAccessPluginStart } from './plugin'; -export type { AssetsDataAccessPluginSetup, AssetsDataAccessPluginStart }; +export type { EntitiesDataAccessPluginSetup, EntitiesDataAccessPluginStart }; export async function plugin(initializerContext: PluginInitializerContext) { - const { AssetsDataAccessPlugin } = await import('./plugin'); - return new AssetsDataAccessPlugin(initializerContext); + const { EntitiesDataAccessPlugin } = await import('./plugin'); + return new EntitiesDataAccessPlugin(initializerContext); } diff --git a/x-pack/plugins/observability_solution/entities_data_access/server/plugin.ts b/x-pack/plugins/observability_solution/entities_data_access/server/plugin.ts new file mode 100644 index 0000000000000..7e71a13f16bec --- /dev/null +++ b/x-pack/plugins/observability_solution/entities_data_access/server/plugin.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/server'; +import { EntitiesPluginSetupDeps, EntitiesPluginStartDeps } from './types'; + +export type EntitiesDataAccessPluginSetup = ReturnType<EntitiesDataAccessPlugin['setup']>; +export type EntitiesDataAccessPluginStart = ReturnType<EntitiesDataAccessPlugin['start']>; + +export class EntitiesDataAccessPlugin implements Plugin { + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup, plugins: EntitiesPluginSetupDeps) {} + + public start(core: CoreStart, plugins: EntitiesPluginStartDeps) {} +} diff --git a/x-pack/plugins/observability_solution/entities_data_access/server/types.ts b/x-pack/plugins/observability_solution/entities_data_access/server/types.ts new file mode 100644 index 0000000000000..4b7a7448df23b --- /dev/null +++ b/x-pack/plugins/observability_solution/entities_data_access/server/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface EntitiesPluginSetupDeps {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface EntitiesPluginStartDeps {} diff --git a/x-pack/plugins/observability_solution/assets_data_access/tsconfig.json b/x-pack/plugins/observability_solution/entities_data_access/tsconfig.json similarity index 100% rename from x-pack/plugins/observability_solution/assets_data_access/tsconfig.json rename to x-pack/plugins/observability_solution/entities_data_access/tsconfig.json diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/delete_index.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/delete_index.ts index a371962833de6..985ca9f3c7a72 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/delete_index.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/delete_index.ts @@ -15,13 +15,11 @@ export async function deleteIndices( logger: Logger ) { try { - const response = await esClient.indices.resolveIndex({ - name: `${generateHistoryIndexName(definition)}.*,${generateLatestIndexName(definition)}`, - }); - const indices = response.indices.map((doc) => doc.name); - if (indices.length) { - await esClient.indices.delete({ index: indices, ignore_unavailable: true }); - } + const indices = [ + `${generateHistoryIndexName(definition)}.*`, + generateLatestIndexName(definition), + ]; + await esClient.indices.delete({ index: indices, ignore_unavailable: true }); } catch (e) { logger.error(`Unable to remove entity definition index [${definition.id}}]`); throw e; diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts index a58019bf236ae..5c3c954b45333 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/install_entity_definition.ts @@ -81,13 +81,13 @@ export async function installEntityDefinition({ await upsertTemplate({ esClient, logger, - template: getEntitiesHistoryIndexTemplateConfig(definition.id), + template: getEntitiesHistoryIndexTemplateConfig(definition), }); installState.indexTemplates.history = true; await upsertTemplate({ esClient, logger, - template: getEntitiesLatestIndexTemplateConfig(definition.id), + template: getEntitiesLatestIndexTemplateConfig(definition), }); installState.indexTemplates.latest = true; diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_history_template.test.ts.snap b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_history_template.test.ts.snap index 94af9f3307f04..464293cc1bca8 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_history_template.test.ts.snap +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_history_template.test.ts.snap @@ -29,6 +29,9 @@ Object { "name": "entities_v1_history_admin-console-services_index_template", "priority": 200, "template": Object { + "aliases": Object { + "entities-service-history": Object {}, + }, "mappings": Object { "_meta": Object { "version": "1.6.0", diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_latest_template.test.ts.snap b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_latest_template.test.ts.snap index b4247098d9498..448286b16d84a 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_latest_template.test.ts.snap +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/__snapshots__/entities_latest_template.test.ts.snap @@ -29,6 +29,9 @@ Object { "name": "entities_v1_latest_admin-console-services_index_template", "priority": 200, "template": Object { + "aliases": Object { + "entities-service-latest": Object {}, + }, "mappings": Object { "_meta": Object { "version": "1.6.0", diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.test.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.test.ts index 11aad78741020..055ebe75cd608 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.test.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.test.ts @@ -10,7 +10,7 @@ import { getEntitiesHistoryIndexTemplateConfig } from './entities_history_templa describe('getEntitiesHistoryIndexTemplateConfig(definitionId)', () => { it('should generate a valid index template', () => { - const template = getEntitiesHistoryIndexTemplateConfig(entityDefinition.id); + const template = getEntitiesHistoryIndexTemplateConfig(entityDefinition); expect(template).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.ts index a0fb4b032a6e1..03f3a3510f627 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_history_template.ts @@ -6,19 +6,22 @@ */ import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { EntityDefinition } from '@kbn/entities-schema'; import { getEntityHistoryIndexTemplateV1 } from '../../../../common/helpers'; import { + ENTITY_BASE_PREFIX, ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, + ENTITY_HISTORY, ENTITY_HISTORY_BASE_COMPONENT_TEMPLATE_V1, ENTITY_HISTORY_INDEX_PREFIX_V1, } from '../../../../common/constants_entities'; import { getCustomHistoryTemplateComponents } from '../../../templates/components/helpers'; export const getEntitiesHistoryIndexTemplateConfig = ( - definitionId: string + definition: EntityDefinition ): IndicesPutIndexTemplateRequest => ({ - name: getEntityHistoryIndexTemplateV1(definitionId), + name: getEntityHistoryIndexTemplateV1(definition.id), _meta: { description: "Index template for indices managed by the Elastic Entity Model's entity discovery framework for the history dataset", @@ -26,16 +29,19 @@ export const getEntitiesHistoryIndexTemplateConfig = ( managed: true, managed_by: 'elastic_entity_model', }, - ignore_missing_component_templates: getCustomHistoryTemplateComponents(definitionId), + ignore_missing_component_templates: getCustomHistoryTemplateComponents(definition.id), composed_of: [ ENTITY_HISTORY_BASE_COMPONENT_TEMPLATE_V1, ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, - ...getCustomHistoryTemplateComponents(definitionId), + ...getCustomHistoryTemplateComponents(definition.id), ], - index_patterns: [`${ENTITY_HISTORY_INDEX_PREFIX_V1}.${definitionId}.*`], + index_patterns: [`${ENTITY_HISTORY_INDEX_PREFIX_V1}.${definition.id}.*`], priority: 200, template: { + aliases: { + [`${ENTITY_BASE_PREFIX}-${definition.type}-${ENTITY_HISTORY}`]: {}, + }, mappings: { _meta: { version: '1.6.0', diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.test.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.test.ts index 72583d941492c..f1012d0bcb7f7 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.test.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.test.ts @@ -10,7 +10,7 @@ import { getEntitiesLatestIndexTemplateConfig } from './entities_latest_template describe('getEntitiesLatestIndexTemplateConfig(definitionId)', () => { it('should generate a valid index template', () => { - const template = getEntitiesLatestIndexTemplateConfig(entityDefinition.id); + const template = getEntitiesLatestIndexTemplateConfig(entityDefinition); expect(template).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.ts b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.ts index 466346f86b44d..7b0093bb4b83f 100644 --- a/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.ts +++ b/x-pack/plugins/observability_solution/entity_manager/server/lib/entities/templates/entities_latest_template.ts @@ -6,19 +6,22 @@ */ import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { EntityDefinition } from '@kbn/entities-schema'; import { getEntityLatestIndexTemplateV1 } from '../../../../common/helpers'; import { + ENTITY_BASE_PREFIX, ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, + ENTITY_LATEST, ENTITY_LATEST_BASE_COMPONENT_TEMPLATE_V1, ENTITY_LATEST_INDEX_PREFIX_V1, } from '../../../../common/constants_entities'; import { getCustomLatestTemplateComponents } from '../../../templates/components/helpers'; export const getEntitiesLatestIndexTemplateConfig = ( - definitionId: string + definition: EntityDefinition ): IndicesPutIndexTemplateRequest => ({ - name: getEntityLatestIndexTemplateV1(definitionId), + name: getEntityLatestIndexTemplateV1(definition.id), _meta: { description: "Index template for indices managed by the Elastic Entity Model's entity discovery framework for the latest dataset", @@ -26,16 +29,19 @@ export const getEntitiesLatestIndexTemplateConfig = ( managed: true, managed_by: 'elastic_entity_model', }, - ignore_missing_component_templates: getCustomLatestTemplateComponents(definitionId), + ignore_missing_component_templates: getCustomLatestTemplateComponents(definition.id), composed_of: [ ENTITY_LATEST_BASE_COMPONENT_TEMPLATE_V1, ENTITY_ENTITY_COMPONENT_TEMPLATE_V1, ENTITY_EVENT_COMPONENT_TEMPLATE_V1, - ...getCustomLatestTemplateComponents(definitionId), + ...getCustomLatestTemplateComponents(definition.id), ], - index_patterns: [`${ENTITY_LATEST_INDEX_PREFIX_V1}.${definitionId}`], + index_patterns: [`${ENTITY_LATEST_INDEX_PREFIX_V1}.${definition.id}`], priority: 200, template: { + aliases: { + [`${ENTITY_BASE_PREFIX}-${definition.type}-${ENTITY_LATEST}`]: {}, + }, mappings: { _meta: { version: '1.6.0', diff --git a/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts b/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts index 72c68d47e9776..8ca585709db21 100644 --- a/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts +++ b/x-pack/plugins/observability_solution/infra/common/formatters/snapshot_metric_formats.ts @@ -29,6 +29,10 @@ export const METRIC_FORMATTERS: MetricFormatters = { formatter: InfraFormatterType.percent, template: '{{value}}', }, + ['cpuTotal']: { + formatter: InfraFormatterType.percent, + template: '{{value}}', + }, ['memory']: { formatter: InfraFormatterType.percent, template: '{{value}}', diff --git a/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts b/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts index 8cbd09470ee71..d4f7f1f7d3635 100644 --- a/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts +++ b/x-pack/plugins/observability_solution/infra/common/http_api/infra/get_infra_metrics.ts @@ -10,6 +10,7 @@ import * as rt from 'io-ts'; export const InfraMetricTypeRT = rt.keyof({ cpu: null, + cpuTotal: null, normalizedLoad1m: null, diskSpaceUsage: null, memory: null, diff --git a/x-pack/plugins/observability_solution/infra/common/inventory_views/defaults.ts b/x-pack/plugins/observability_solution/infra/common/inventory_views/defaults.ts index 305c322ba0d23..047b79cada84e 100644 --- a/x-pack/plugins/observability_solution/infra/common/inventory_views/defaults.ts +++ b/x-pack/plugins/observability_solution/infra/common/inventory_views/defaults.ts @@ -18,7 +18,7 @@ export const staticInventoryViewAttributes: InventoryViewAttributes = { isDefault: false, isStatic: true, metric: { - type: 'cpu', + type: 'cpuTotal', }, groupBy: [], nodeType: 'host', diff --git a/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts b/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts index 0561ea0c3add1..75a8b9cb2d0f1 100644 --- a/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts +++ b/x-pack/plugins/observability_solution/infra/common/snapshot_metric_i18n.ts @@ -12,10 +12,14 @@ import type { InventoryItemType, SnapshotMetricType } from '@kbn/metrics-data-ac // Lowercase versions of all metrics, for when they need to be used in the middle of a sentence; // these may need to be translated differently depending on language, e.g. still capitalizing "CPU" const TranslationsLowercase = { - CPUUsage: i18n.translate('xpack.infra.waffle.metricOptions.cpuUsageText', { + CPUUsageTotal: i18n.translate('xpack.infra.waffle.metricOptions.cpuUsageTotalText', { defaultMessage: 'CPU usage', }), + CPUUsageLegacy: i18n.translate('xpack.infra.waffle.metricOptions.cpuUsageLegacyText', { + defaultMessage: 'CPU usage (legacy)', + }), + MemoryUsage: i18n.translate('xpack.infra.waffle.metricOptions.memoryUsageText', { defaultMessage: 'memory usage', }), @@ -109,10 +113,16 @@ export const toMetricOpt = ( nodeType?: InventoryItemType ): { text: string; textLC: string; value: SnapshotMetricType } | undefined => { switch (metric) { + case 'cpuTotal': + return { + text: Translations.CPUUsageTotal, + textLC: TranslationsLowercase.CPUUsageTotal, + value: 'cpuTotal', + }; case 'cpu': return { - text: Translations.CPUUsage, - textLC: TranslationsLowercase.CPUUsage, + text: Translations.CPUUsageLegacy, + textLC: TranslationsLowercase.CPUUsageLegacy, value: 'cpu', }; case 'memory': diff --git a/x-pack/plugins/observability_solution/infra/common/source_configuration/defaults.ts b/x-pack/plugins/observability_solution/infra/common/source_configuration/defaults.ts index ac3724c80d70a..05988909e3f36 100644 --- a/x-pack/plugins/observability_solution/infra/common/source_configuration/defaults.ts +++ b/x-pack/plugins/observability_solution/infra/common/source_configuration/defaults.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { LOGS_INDEX_PATTERN, METRICS_INDEX_PATTERN } from '../constants'; +import { METRICS_INDEX_PATTERN } from '../constants'; import { InfraSourceConfiguration } from './source_configuration'; export const defaultSourceConfiguration: InfraSourceConfiguration = { @@ -13,8 +13,7 @@ export const defaultSourceConfiguration: InfraSourceConfiguration = { description: '', metricAlias: METRICS_INDEX_PATTERN, logIndices: { - type: 'index_name', - indexName: LOGS_INDEX_PATTERN, + type: 'kibana_advanced_setting', }, inventoryDefaultView: '0', metricsExplorerDefaultView: '0', diff --git a/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts b/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts index 116c30d8274e3..e9779c192788d 100644 --- a/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts +++ b/x-pack/plugins/observability_solution/infra/common/source_configuration/source_configuration.ts @@ -80,7 +80,16 @@ export const logIndexNameReferenceRT = rt.type({ }); export type LogIndexNameReference = rt.TypeOf<typeof logIndexNameReferenceRT>; -export const logIndexReferenceRT = rt.union([logIndexPatternReferenceRT, logIndexNameReferenceRT]); +// Kibana advanced setting +export const logSourcesKibanaAdvancedSettingRT = rt.type({ + type: rt.literal('kibana_advanced_setting'), +}); + +export const logIndexReferenceRT = rt.union([ + logIndexPatternReferenceRT, + logIndexNameReferenceRT, + logSourcesKibanaAdvancedSettingRT, +]); export type LogIndexReference = rt.TypeOf<typeof logIndexReferenceRT>; export const SourceConfigurationRT = rt.type({ diff --git a/x-pack/plugins/observability_solution/infra/kibana.jsonc b/x-pack/plugins/observability_solution/infra/kibana.jsonc index 973acf3e75d55..cf73b1636d93e 100644 --- a/x-pack/plugins/observability_solution/infra/kibana.jsonc +++ b/x-pack/plugins/observability_solution/infra/kibana.jsonc @@ -34,7 +34,8 @@ "unifiedSearch", "usageCollection", "visTypeTimeseries", - "apmDataAccess" + "apmDataAccess", + "logsDataAccess" ], "optionalPlugins": [ "spaces", diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx index c8e537621c81b..e3ae562fe80f3 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx @@ -772,6 +772,7 @@ export const nodeTypes: { [key: string]: any } = { const metricUnit: Record<string, { label: string }> = { count: { label: '' }, cpu: { label: '%' }, + cpuTotal: { label: '%' }, memory: { label: '%' }, rx: { label: 'bits/s' }, tx: { label: 'bits/s' }, diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression_chart.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression_chart.tsx index 5bea3d01ca231..b2b280d182a4b 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression_chart.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression_chart.tsx @@ -231,6 +231,7 @@ const convertMetricValue = (metric: SnapshotMetricType, value: number) => { }; const converters: Record<string, (n: number) => number> = { cpu: (n) => Number(n) / 100, + cpuTotal: (n) => Number(n) / 100, memory: (n) => Number(n) / 100, tx: (n) => Number(n) / 8, rx: (n) => Number(n) / 8, diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/hooks/use_inventory_alert_prefill.ts b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/hooks/use_inventory_alert_prefill.ts index 2ba9f1f36180a..4c3ade788e25e 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/hooks/use_inventory_alert_prefill.ts +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/hooks/use_inventory_alert_prefill.ts @@ -15,7 +15,7 @@ import { export const useInventoryAlertPrefill = () => { const [nodeType, setNodeType] = useState<InventoryItemType>('host'); const [filterQuery, setFilterQuery] = useState<string | undefined>(); - const [metric, setMetric] = useState<SnapshotMetricInput>({ type: 'cpu' }); + const [metric, setMetric] = useState<SnapshotMetricInput>({ type: 'cpuTotal' }); const [customMetrics, setCustomMetrics] = useState<SnapshotCustomMetricInput[]>([]); // only shows for AWS when there are regions info const [region, setRegion] = useState(''); diff --git a/x-pack/plugins/observability_solution/infra/public/common/visualizations/translations.ts b/x-pack/plugins/observability_solution/infra/public/common/visualizations/translations.ts index 1a0446584e944..de1e8e16d1d51 100644 --- a/x-pack/plugins/observability_solution/infra/public/common/visualizations/translations.ts +++ b/x-pack/plugins/observability_solution/infra/public/common/visualizations/translations.ts @@ -17,7 +17,7 @@ export const METRICS_TOOLTIP = { cpuUsage: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.cpuUsage', { defaultMessage: - 'Percentage of CPU time spent in states other than Idle and IOWait, normalized by the number of CPU cores. This includes both time spent on user space and kernel space.', + 'Average of percentage of CPU time spent in states other than Idle and IOWait, normalised by the number of CPU cores. Includes both time spent on user space and kernel space. 100% means all CPUs of the host are busy.', }), diskUsage: i18n.translate('xpack.infra.hostsViewPage.metrics.tooltip.diskSpaceUsage', { defaultMessage: 'Percentage of disk space used.', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_form_state.ts b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_form_state.ts index 46b5ce97ee4ca..581d8d3011cd6 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_form_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_form_state.ts @@ -12,6 +12,8 @@ import { LogDataViewReference, LogIndexNameReference, logIndexNameReferenceRT, + LogSourcesKibanaAdvancedSettingReference, + logSourcesKibanaAdvancedSettingRT, } from '@kbn/logs-shared-plugin/common'; import { useKibanaIndexPatternService } from '../../../hooks/use_kibana_index_patterns'; import { useFormElement } from './form_elements'; @@ -22,7 +24,11 @@ import { validateStringNoSpaces, } from './validation_errors'; -export type LogIndicesFormState = LogIndexNameReference | LogDataViewReference | undefined; +export type LogIndicesFormState = + | LogIndexNameReference + | LogDataViewReference + | LogSourcesKibanaAdvancedSettingReference + | undefined; export const useLogIndicesFormElement = (initialValue: LogIndicesFormState) => { const indexPatternService = useKibanaIndexPatternService(); @@ -35,6 +41,8 @@ export const useLogIndicesFormElement = (initialValue: LogIndicesFormState) => { () => async (logIndices) => { if (logIndices == null) { return validateStringNotEmpty('log data view', ''); + } else if (logSourcesKibanaAdvancedSettingRT.is(logIndices)) { + return []; } else if (logIndexNameReferenceRT.is(logIndices)) { return [ ...validateStringNotEmpty('log indices', logIndices.indexName), diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_panel.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_panel.tsx index bcde2cf84bb4a..0aad03315c8e1 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_panel.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/indices_configuration_panel.tsx @@ -14,6 +14,7 @@ import { LogDataViewReference, logDataViewReferenceRT, LogIndexReference, + logSourcesKibanaAdvancedSettingRT, } from '@kbn/logs-shared-plugin/common'; import { EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,6 +28,7 @@ import { FormElement, isFormElementForType } from './form_elements'; import { IndexNamesConfigurationPanel } from './index_names_configuration_panel'; import { IndexPatternConfigurationPanel } from './index_pattern_configuration_panel'; import { FormValidationError } from './validation_errors'; +import { KibanaAdvancedSettingConfigurationPanel } from './kibana_advanced_setting_configuration_panel'; export const IndicesConfigurationPanel = React.memo<{ isLoading: boolean; @@ -75,6 +77,17 @@ export const IndicesConfigurationPanel = React.memo<{ }); }, [indicesFormElement, trackChangeIndexSourceType]); + const changeToKibanaAdvancedSettingType = useCallback(() => { + // This is always a readonly value, synced with the setting, we just reset back to the correct type. + indicesFormElement.updateValue(() => ({ + type: 'kibana_advanced_setting', + })); + + trackChangeIndexSourceType({ + metric: 'configuration_switch_to_kibana_advanced_setting_reference', + }); + }, [indicesFormElement, trackChangeIndexSourceType]); + useEffect(() => { const getNumberOfInfraRules = async () => { if (http) { @@ -107,6 +120,34 @@ export const IndicesConfigurationPanel = React.memo<{ ), }} > + {' '} + <EuiCheckableCard + id="kibanaAdvancedSetting" + label={ + <EuiTitle size="xs"> + <h2> + <FormattedMessage + id="xpack.infra.logSourceConfiguration.kibanaAdvancedSettingSectionTitle" + defaultMessage="Kibana log sources advanced setting" + /> + </h2> + </EuiTitle> + } + name="kibanaAdvancedSetting" + value="kibanaAdvancedSetting" + checked={isKibanaAdvancedSettingFormElement(indicesFormElement)} + onChange={changeToKibanaAdvancedSettingType} + disabled={isReadOnly} + > + {isKibanaAdvancedSettingFormElement(indicesFormElement) && ( + <KibanaAdvancedSettingConfigurationPanel + isLoading={isLoading} + isReadOnly={isReadOnly} + advancedSettingFormElement={indicesFormElement} + /> + )} + </EuiCheckableCard> + <EuiSpacer size="m" /> <EuiCheckableCard id="dataView" label={ @@ -114,7 +155,7 @@ export const IndicesConfigurationPanel = React.memo<{ <h2> <FormattedMessage id="xpack.infra.logSourceConfiguration.dataViewSectionTitle" - defaultMessage="Data view (recommended)" + defaultMessage="Data view (deprecated)" /> </h2> </EuiTitle> @@ -134,15 +175,14 @@ export const IndicesConfigurationPanel = React.memo<{ )} </EuiCheckableCard> <EuiSpacer size="m" /> - <EuiCheckableCard id="indexNames" label={ <EuiTitle size="xs"> <h2> <FormattedMessage - id="xpack.infra.sourceConfiguration.indicesSectionTitle" - defaultMessage="Indices" + id="xpack.infra.sourceConfiguration.logsIndicesSectionTitle" + defaultMessage="Indices (deprecated)" /> </h2> </EuiTitle> @@ -152,6 +192,7 @@ export const IndicesConfigurationPanel = React.memo<{ checked={isIndexNamesFormElement(indicesFormElement)} onChange={changeToIndexNameType} disabled={isReadOnly} + data-test-subj="logIndicesCheckableCard" > {isIndexNamesFormElement(indicesFormElement) && ( <IndexNamesConfigurationPanel @@ -201,3 +242,7 @@ const isDataViewFormElement = isFormElementForType( ); const isIndexNamesFormElement = isFormElementForType(logIndexNameReferenceRT.is); + +const isKibanaAdvancedSettingFormElement = isFormElementForType( + logSourcesKibanaAdvancedSettingRT.is +); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/kibana_advanced_setting_configuration_panel.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/kibana_advanced_setting_configuration_panel.tsx new file mode 100644 index 0000000000000..1d7d4ad997407 --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/kibana_advanced_setting_configuration_panel.tsx @@ -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 { EuiDescribedFormGroup, EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; +import { LogSourcesKibanaAdvancedSettingReference } from '@kbn/logs-shared-plugin/common'; +import { ApplicationStart } from '@kbn/core-application-browser'; +import { EuiLink } from '@elastic/eui'; +import { useTrackedPromise } from '../../../hooks/use_tracked_promise'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; +import { FormElement } from './form_elements'; +import { getFormRowProps } from './form_field_props'; +import { FormValidationError } from './validation_errors'; + +function getKibanaAdvancedSettingsHref(application: ApplicationStart) { + return application.getUrlForApp('management', { + path: `/kibana/settings?query=${encodeURIComponent('Log sources')}`, + }); +} + +export const KibanaAdvancedSettingConfigurationPanel: React.FC<{ + isLoading: boolean; + isReadOnly: boolean; + advancedSettingFormElement: FormElement< + LogSourcesKibanaAdvancedSettingReference, + FormValidationError + >; +}> = ({ isLoading, isReadOnly, advancedSettingFormElement }) => { + const { + services: { application, logsDataAccess }, + } = useKibanaContextForPlugin(); + + useTrackPageview({ app: 'infra_logs', path: 'log_source_configuration_kibana_advanced_setting' }); + useTrackPageview({ + app: 'infra_logs', + path: 'log_source_configuration_kibana_advanced_setting', + delay: 15000, + }); + + const advancedSettingsHref = useMemo( + () => getKibanaAdvancedSettingsHref(application), + [application] + ); + + const [logSourcesSettingValue, setLogSourcesSettingValue] = useState<string | undefined>( + undefined + ); + + const [getLogSourcesRequest, getLogSources] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await logsDataAccess.services.logSourcesService.getLogSources(); + }, + onResolve: (response) => { + setLogSourcesSettingValue(response.map((logSource) => logSource.indexPattern).join(',')); + }, + }, + [] + ); + + const isLoadingLogSourcesSetting = useMemo( + () => getLogSourcesRequest.state === 'pending', + [getLogSourcesRequest.state] + ); + + useEffect(() => { + getLogSources(); + }, [getLogSources]); + + return ( + <> + <EuiDescribedFormGroup + title={ + <h4> + <FormattedMessage + id="xpack.infra.sourceConfiguration.logSourcesSettingTitle" + defaultMessage="Advanced setting" + /> + </h4> + } + description={ + <FormattedMessage + id="xpack.infra.sourceConfiguration.logSourcesSettingDescription" + defaultMessage="This value is synchronised with the Kibana log sources advanced setting. It can be changed via the {advancedSettingsLink}." + values={{ + advancedSettingsLink: ( + <EuiLink + data-test-subj="xpack.infra.sourceConfiguration.logSourcesSettingLink" + href={advancedSettingsHref} + > + <FormattedMessage + id="xpack.infra.sourceConfiguration.logSourcesSettingLinkText" + defaultMessage="advanced settings page" + /> + </EuiLink> + ), + }} + /> + } + > + <EuiFormRow + fullWidth + helpText={ + <FormattedMessage + id="xpack.infra.sourceConfiguration.logSourcesSettingValue" + defaultMessage="The current setting value" + /> + } + label={ + <FormattedMessage + id="xpack.infra.sourceConfiguration.logSourcesSettingLabel" + defaultMessage="Log sources advanced setting" + /> + } + {...getFormRowProps(advancedSettingFormElement)} + > + <EuiFieldText + data-test-subj="logSourcesSettingInput" + fullWidth + disabled={isLoading} + isLoading={isLoadingLogSourcesSetting} + readOnly={true} + value={logSourcesSettingValue} + isInvalid={false} + /> + </EuiFormRow> + </EuiDescribedFormGroup> + </> + ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_form_state.tsx b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_form_state.tsx index 5ea244e5e0a52..01035f8259a0f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_form_state.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/source_configuration_form_state.tsx @@ -19,8 +19,7 @@ export const useLogSourceConfigurationFormState = (logViewAttributes?: LogViewAt useMemo( () => logViewAttributes?.logIndices ?? { - type: 'index_name', - indexName: '', + type: 'kibana_advanced_setting', }, [logViewAttributes] ) diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts index 5df80a9e3634d..2596d984c6b3a 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts @@ -41,7 +41,7 @@ const mockHostNode: InfraAssetMetricsItem[] = [ { metrics: [ { - name: 'cpu', + name: 'cpuTotal', value: 0.6353277777777777, }, { @@ -79,7 +79,7 @@ const mockHostNode: InfraAssetMetricsItem[] = [ { metrics: [ { - name: 'cpu', + name: 'cpuTotal', value: 0.8647805555555556, }, { @@ -169,7 +169,7 @@ describe('useHostTable hook', () => { rx: 252456.92916666667, tx: 252758.425, memory: 0.94525, - cpu: 0.6353277777777777, + cpuTotal: 0.6353277777777777, diskSpaceUsage: 0.2040001, memoryFree: 34359.738368, normalizedLoad1m: 239.2040001, @@ -187,7 +187,7 @@ describe('useHostTable hook', () => { rx: 95.86339715321859, tx: 110.38566859563191, memory: 0.5400000214576721, - cpu: 0.8647805555555556, + cpuTotal: 0.8647805555555556, diskSpaceUsage: 0.5400000214576721, memoryFree: 9.194304, normalizedLoad1m: 100, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index cdafb71784ab9..ed5b29d10b9ed 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -287,10 +287,10 @@ export const useHostsTable = () => { /> ), width: metricColumnsWidth, - field: 'cpu', + field: 'cpuTotal', sortable: true, 'data-test-subj': 'hostsView-tableRow-cpuUsage', - render: (avg: number) => formatMetric('cpu', avg), + render: (avg: number) => formatMetric('cpuTotal', avg), align: 'right', }, { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts index 9ed1989cfa85f..655db7941d753 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_view.ts @@ -26,7 +26,7 @@ import { import { StringDateRange } from './use_unified_search_url_state'; const HOST_TABLE_METRICS: Array<{ type: InfraAssetMetricType }> = [ - { type: 'cpu' }, + { type: 'cpuTotal' }, { type: 'diskSpaceUsage' }, { type: 'memory' }, { type: 'memoryFree' }, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap index f3368be815f1b..70b15408b38df 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap @@ -26,6 +26,22 @@ exports[`ConditionalToolTip renders correctly 1`] = ` 10% </div> </div> + <div + class="euiFlexGroup emotion-euiFlexGroup-responsive-s-flexStart-stretch-row" + > + <div + class="euiFlexItem eui-textTruncate eui-displayBlock emotion-euiFlexItem-grow-1" + data-test-subj="conditionalTooltipContent-metric" + > + CPU usage (legacy) + </div> + <div + class="euiFlexItem emotion-euiFlexItem-growZero" + data-test-subj="conditionalTooltipContent-value" + > + 10% + </div> + </div> <div class="euiFlexGroup emotion-euiFlexGroup-responsive-s-flexStart-stretch-row" > diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx index 439999d5b143f..b15779cbd2e9e 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx @@ -30,7 +30,7 @@ const NODE: InfraWaffleMapNode = { id: 'host-01', name: 'host-01', path: [{ value: 'host-01', label: 'host-01' }], - metrics: [{ name: 'cpu' }], + metrics: [{ name: 'cpuTotal' }], }; export const nextTick = () => new Promise((res) => process.nextTick(res)); @@ -45,6 +45,7 @@ describe('ConditionalToolTip', () => { name: 'host-01', path: [{ label: 'host-01', value: 'host-01', ip: '192.168.1.10' }], metrics: [ + { name: 'cpuTotal', value: 0.1, avg: 0.4, max: 0.7 }, { name: 'cpu', value: 0.1, avg: 0.4, max: 0.7 }, { name: 'memory', value: 0.8, avg: 0.8, max: 1 }, { name: 'txV2', value: 1000000, avg: 1000000, max: 1000000 }, @@ -78,13 +79,14 @@ describe('ConditionalToolTip', () => { }, }); const expectedMetrics = [ + { type: 'cpuTotal' }, { type: 'cpu' }, { type: 'memory' }, { type: 'txV2' }, { type: 'rxV2' }, { aggregation: 'avg', - field: 'host.cpu.pct', + field: 'host.cpuTotal.pct', id: 'cedd6ca0-5775-11eb-a86f-adb714b6c486', label: 'My Custom Label', type: 'custom', @@ -139,11 +141,11 @@ const mockedUseWaffleOptionsContexReturnValue: ReturnType<typeof useWaffleOption nodeType: 'host', customOptions: [], view: 'map', - metric: { type: 'cpu' }, + metric: { type: 'cpuTotal' }, customMetrics: [ { aggregation: 'avg', - field: 'host.cpu.pct', + field: 'host.cpuTotal.pct', id: 'cedd6ca0-5775-11eb-a86f-adb714b6c486', label: 'My Custom Label', type: 'custom', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts index 24b9dbc8a1ef3..561ff4e4e749d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts @@ -33,7 +33,7 @@ export const DEFAULT_LEGEND: WaffleLegendOptions = { }; export const DEFAULT_WAFFLE_OPTIONS_STATE: WaffleOptionsState = { - metric: { type: 'cpu' }, + metric: { type: 'cpuTotal' }, groupBy: [], nodeType: 'host', view: 'map', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts index 056109587d36d..6786cb427632f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts @@ -31,6 +31,10 @@ const METRIC_FORMATTERS: MetricFormatters = { formatter: InfraFormatterType.percent, template: '{{value}}', }, + cpuTotal: { + formatter: InfraFormatterType.percent, + template: '{{value}}', + }, diskIOReadBytes: { formatter: InfraFormatterType.bytes, template: '{{value}}/s', diff --git a/x-pack/plugins/observability_solution/infra/public/types.ts b/x-pack/plugins/observability_solution/infra/public/types.ts index 982f191941ab4..d7a7d339f41be 100644 --- a/x-pack/plugins/observability_solution/infra/public/types.ts +++ b/x-pack/plugins/observability_solution/infra/public/types.ts @@ -49,6 +49,7 @@ import type { CloudSetup } from '@kbn/cloud-plugin/public'; import type { LicenseManagementUIPluginSetup } from '@kbn/license-management-plugin/public'; import type { ServerlessPluginStart } from '@kbn/serverless/public'; import type { DashboardStart } from '@kbn/dashboard-plugin/public'; +import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; import type { UnwrapPromise } from '../common/utility_types'; import { InventoryViewsServiceStart } from './services/inventory_views'; import { MetricsExplorerViewsServiceStart } from './services/metrics_explorer_views'; @@ -95,6 +96,7 @@ export interface InfraClientStartDeps { embeddable?: EmbeddableStart; lens: LensPublicStart; logsShared: LogsSharedClientStartExports; + logsDataAccess: LogsDataAccessPluginStart; ml?: MlPluginStart; observability: ObservabilityPublicStart; observabilityShared: ObservabilitySharedPluginStart; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts index 8fe4101d7ebca..7b068424a7cc8 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/adapters/framework/adapter_types.ts @@ -38,6 +38,7 @@ import { ApmDataAccessPluginSetup, ApmDataAccessPluginStart, } from '@kbn/apm-data-access-plugin/server'; +import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/server'; export interface InfraServerPluginSetupDeps { alerting: AlertingPluginContract; @@ -64,6 +65,7 @@ export interface InfraServerPluginStartDeps { profilingDataAccess?: ProfilingDataAccessPluginStart; ruleRegistry: RuleRegistryPluginStartContract; apmDataAccess: ApmDataAccessPluginStart; + logsDataAccess: LogsDataAccessPluginStart; } export interface CallWithRequestParams extends estypes.RequestBase { diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts index d109669aa90f0..f76a6e82e67d5 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts @@ -22,6 +22,7 @@ import { ConditionResult } from './evaluate_condition'; import { InfraBackendLibs } from '../../infra_types'; import { infraPluginMock } from '../../../mocks'; import { logsSharedPluginMock } from '@kbn/logs-shared-plugin/server/mocks'; +import { createLogSourcesServiceMock } from '@kbn/logs-data-access-plugin/common/services/log_sources_service/log_sources_service.mocks'; jest.mock('./evaluate_condition', () => ({ evaluateCondition: jest.fn() })); @@ -115,7 +116,16 @@ const mockLibs = { }, getStartServices: () => [ null, - { logsShared: logsSharedPluginMock.createStartContract() }, + { + logsShared: logsSharedPluginMock.createStartContract(), + logsDataAccess: { + services: { + logSourcesServiceFactory: { + getLogSourcesService: () => createLogSourcesServiceMock(), + }, + }, + }, + }, infraPluginMock.createStartContract(), ], configuration: createMockStaticConfiguration({}), diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index e6ed1750eea3a..80da1034df5ac 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -165,9 +165,13 @@ export const createInventoryMetricThresholdExecutor = } const source = await libs.sources.getSourceConfiguration(savedObjectsClient, sourceId); - const [, { logsShared }] = await libs.getStartServices(); + const [, { logsShared, logsDataAccess }] = await libs.getStartServices(); + + const logSourcesService = + logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService(savedObjectsClient); + const logQueryFields: LogQueryFields | undefined = await logsShared.logViews - .getClient(savedObjectsClient, esClient) + .getClient(savedObjectsClient, esClient, logSourcesService) .getResolvedLogView({ type: 'log-view-reference', logViewId: sourceId, diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/convert_metric_value.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/convert_metric_value.ts index d2301072d4854..9866665e423af 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/convert_metric_value.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/convert_metric_value.ts @@ -17,6 +17,7 @@ export const convertMetricValue = (metric: SnapshotMetricType, value: number) => }; const converters: Record<string, (n: number) => number> = { cpu: (n) => Number(n) / 100, + cpuTotal: (n) => Number(n) / 100, memory: (n) => Number(n) / 100, tx: (n) => Number(n) / 8, rx: (n) => Number(n) / 8, diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index c757f374f8855..0ac06618a3ba2 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -203,13 +203,16 @@ export const createLogThresholdExecutor = } }; - const [, { logsShared }] = await libs.getStartServices(); + const [, { logsShared, logsDataAccess }] = await libs.getStartServices(); try { const validatedParams = decodeOrThrow(ruleParamsRT)(params); + const logSourcesService = + logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService(savedObjectsClient); + const { indices, timestampField, runtimeMappings } = await logsShared.logViews - .getClient(savedObjectsClient, scopedClusterClient.asCurrentUser) + .getClient(savedObjectsClient, scopedClusterClient.asCurrentUser, logSourcesService) .getResolvedLogView(validatedParams.logView); if (!isRatioRuleParams(validatedParams)) { diff --git a/x-pack/plugins/observability_solution/infra/server/lib/sources/types.ts b/x-pack/plugins/observability_solution/infra/server/lib/sources/types.ts index 22cc5108c35c9..9d32269548632 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/sources/types.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/sources/types.ts @@ -61,9 +61,14 @@ export const logIndexNameSavedObjectReferenceRT = rt.type({ indexName: rt.string, }); +export const kibanaAdvancedSettingSavedObjectReferenceRT = rt.type({ + type: rt.literal('kibana_advanced_setting'), +}); + export const logIndexSavedObjectReferenceRT = rt.union([ logIndexPatternSavedObjectReferenceRT, logIndexNameSavedObjectReferenceRT, + kibanaAdvancedSettingSavedObjectReferenceRT, ]); export const SourceConfigurationSavedObjectAttributesRT = rt.type({ diff --git a/x-pack/plugins/observability_solution/infra/server/routes/infra/lib/host/get_all_hosts.ts b/x-pack/plugins/observability_solution/infra/server/routes/infra/lib/host/get_all_hosts.ts index 7172a0c3da4d4..15bc0df8d76e5 100644 --- a/x-pack/plugins/observability_solution/infra/server/routes/infra/lib/host/get_all_hosts.ts +++ b/x-pack/plugins/observability_solution/infra/server/routes/infra/lib/host/get_all_hosts.ts @@ -23,8 +23,8 @@ export const getAllHosts = async ( const result = (response.aggregations?.nodes.buckets ?? []) .sort((a, b) => { - const aValue = getMetricValue(a?.cpu) ?? 0; - const bValue = getMetricValue(b?.cpu) ?? 0; + const aValue = getMetricValue(a?.cpuTotal) ?? 0; + const bValue = getMetricValue(b?.cpuTotal) ?? 0; return bValue - aValue; }) .map((bucket) => { diff --git a/x-pack/plugins/observability_solution/infra/server/routes/ip_to_hostname.ts b/x-pack/plugins/observability_solution/infra/server/routes/ip_to_hostname.ts index 7bafa78750690..0f8c00c51b0db 100644 --- a/x-pack/plugins/observability_solution/infra/server/routes/ip_to_hostname.ts +++ b/x-pack/plugins/observability_solution/infra/server/routes/ip_to_hostname.ts @@ -52,10 +52,10 @@ export const initIpToHostName = ({ framework }: InfraBackendLibs) => { } const hostDoc = first(hits.hits)!; return response.ok({ body: { host: hostDoc._source.host.name } }); - } catch ({ statusCode = 500, message = 'Unknown error occurred' }) { + } catch (error) { return response.customError({ - statusCode, - body: { message }, + statusCode: error.statusCode || 500, + body: { message: error.message || 'Unknown error occurred' }, }); } } diff --git a/x-pack/plugins/observability_solution/infra/tsconfig.json b/x-pack/plugins/observability_solution/infra/tsconfig.json index 82f125911abef..e0e6750550fbb 100644 --- a/x-pack/plugins/observability_solution/infra/tsconfig.json +++ b/x-pack/plugins/observability_solution/infra/tsconfig.json @@ -106,8 +106,10 @@ "@kbn/presentation-containers", "@kbn/deeplinks-observability", "@kbn/event-annotation-common", + "@kbn/logs-data-access-plugin", "@kbn/core-analytics-browser", "@kbn/observability-alerting-rule-utils", + "@kbn/core-application-browser", "@kbn/shared-ux-page-no-data-types" ], "exclude": ["target/**/*"] diff --git a/x-pack/plugins/observability_solution/investigate/common/index.ts b/x-pack/plugins/observability_solution/investigate/common/index.ts index 467a892ca55d1..d97a5c9e3c4d2 100644 --- a/x-pack/plugins/observability_solution/investigate/common/index.ts +++ b/x-pack/plugins/observability_solution/investigate/common/index.ts @@ -4,13 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -export type { - Investigation, - InvestigationRevision, - InvestigateWidget, - InvestigateWidgetCreate, - WorkflowBlock, -} from './types'; +export type { Investigation, InvestigateWidget, InvestigateWidgetCreate } from './types'; export { mergePlainObjects } from './utils/merge_plain_objects'; diff --git a/x-pack/plugins/observability_solution/investigate/common/types.ts b/x-pack/plugins/observability_solution/investigate/common/types.ts index 756e346ffe7af..e8ca999e8af7f 100644 --- a/x-pack/plugins/observability_solution/investigate/common/types.ts +++ b/x-pack/plugins/observability_solution/investigate/common/types.ts @@ -5,17 +5,14 @@ * 2.0. */ -import type { EuiThemeComputed } from '@elastic/eui'; -import type { Filter } from '@kbn/es-query'; -import type { DeepPartial, PickByValue } from 'utility-types'; import type { AuthenticatedUser } from '@kbn/core/public'; +import type { DeepPartial } from 'utility-types'; export interface GlobalWidgetParameters { timeRange: { from: string; to: string; }; - filters: Filter[]; } export enum InvestigateWidgetColumnSpan { @@ -25,19 +22,13 @@ export enum InvestigateWidgetColumnSpan { Four = 4, } -export interface InvestigationRevision { - id: string; - items: InvestigateWidget[]; - parameters: GlobalWidgetParameters; -} - export interface Investigation { id: string; '@timestamp': number; user: AuthenticatedUser; - revisions: InvestigationRevision[]; title: string; - revision: string; + items: InvestigateWidget[]; + parameters: GlobalWidgetParameters; } export interface InvestigateWidget< @@ -55,22 +46,11 @@ export interface InvestigateWidget< description?: string; columns: InvestigateWidgetColumnSpan; rows: number; - locked: boolean; } export type InvestigateWidgetCreate<TParameters extends Record<string, any> = {}> = Pick< InvestigateWidget, - 'title' | 'description' | 'columns' | 'rows' | 'type' | 'locked' + 'title' | 'description' | 'columns' | 'rows' | 'type' > & { parameters: DeepPartial<GlobalWidgetParameters> & TParameters; }; - -export interface WorkflowBlock { - id: string; - content?: string; - description?: string; - loading: boolean; - onClick?: () => void; - color?: keyof PickByValue<EuiThemeComputed<{}>['colors'], string>; - children?: React.ReactNode; -} diff --git a/x-pack/plugins/observability_solution/investigate/public/create_widget.ts b/x-pack/plugins/observability_solution/investigate/public/create_widget.ts index 7528565d06165..29058298c674d 100644 --- a/x-pack/plugins/observability_solution/investigate/public/create_widget.ts +++ b/x-pack/plugins/observability_solution/investigate/public/create_widget.ts @@ -12,7 +12,7 @@ import { GlobalWidgetParameters } from '../common/types'; type MakePartial<T extends Record<string, any>, K extends keyof T> = Omit<T, K> & DeepPartial<Pick<T, K>>; -type PredefinedKeys = 'rows' | 'columns' | 'locked' | 'type'; +type PredefinedKeys = 'rows' | 'columns' | 'type'; type AllowedDefaultKeys = 'rows' | 'columns'; @@ -31,7 +31,6 @@ export function createWidgetFactory<TParameters extends Record<string, any>>( return { rows: 12, columns: InvestigateWidgetColumnSpan.Four, - locked: false, type, ...defaults, ...widgetCreate, diff --git a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigate_widget.tsx b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigate_widget.tsx index 3ace787c71439..a29614f74782b 100644 --- a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigate_widget.tsx +++ b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigate_widget.tsx @@ -5,18 +5,13 @@ * 2.0. */ import { useContext, createContext } from 'react'; -import type { InvestigateWidgetCreate, WorkflowBlock } from '../../common'; - -type UnregisterBlocksFunction = () => void; +import type { InvestigateWidgetCreate } from '../../common'; export interface UseInvestigateWidgetApi< TParameters extends Record<string, any> = {}, TData extends Record<string, any> = {} > { onWidgetAdd: (create: InvestigateWidgetCreate) => Promise<void>; - blocks: { - publish: (blocks: WorkflowBlock[]) => UnregisterBlocksFunction; - }; } const InvestigateWidgetApiContext = createContext<UseInvestigateWidgetApi | undefined>(undefined); diff --git a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/create_new_investigation.ts b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/create_new_investigation.ts index 51c0a68c782c8..7eab4a192ca3c 100644 --- a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/create_new_investigation.ts +++ b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/create_new_investigation.ts @@ -8,7 +8,7 @@ import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { v4 } from 'uuid'; import { i18n } from '@kbn/i18n'; -import type { Investigation, InvestigationRevision } from '../../../common'; +import type { Investigation } from '../../../common'; import { GlobalWidgetParameters } from '../../../common/types'; export function createNewInvestigation({ @@ -20,14 +20,6 @@ export function createNewInvestigation({ user: AuthenticatedUser; globalWidgetParameters: GlobalWidgetParameters; }): Investigation { - const revisionId = v4(); - - const revision: InvestigationRevision = { - id: revisionId, - items: [], - parameters: globalWidgetParameters, - }; - return { '@timestamp': new Date().getTime(), user, @@ -35,7 +27,7 @@ export function createNewInvestigation({ title: i18n.translate('xpack.investigate.newInvestigationTitle', { defaultMessage: 'New investigation', }), - revision: revisionId, - revisions: [revision], + items: [], + parameters: globalWidgetParameters, }; } diff --git a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/index.tsx b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/index.tsx index 4bf2e10cad4be..069255e20e233 100644 --- a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/index.tsx +++ b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/index.tsx @@ -6,17 +6,12 @@ */ import type { AuthenticatedUser, NotificationsStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; -import { last, omit, pull } from 'lodash'; +import { pull } from 'lodash'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { v4 } from 'uuid'; import type { GlobalWidgetParameters } from '../..'; -import type { - InvestigateWidget, - InvestigateWidgetCreate, - Investigation, - WorkflowBlock, -} from '../../../common'; +import type { InvestigateWidget, InvestigateWidgetCreate, Investigation } from '../../../common'; import type { WidgetDefinition } from '../../types'; import { InvestigateWidgetApiContextProvider, @@ -24,56 +19,28 @@ import { } from '../use_investigate_widget'; import { useLocalStorage } from '../use_local_storage'; import { createNewInvestigation } from './create_new_investigation'; -import { - createInvestigationStore, - StatefulInvestigation, - StatefulInvestigationRevision, -} from './investigation_store'; +import { StatefulInvestigation, createInvestigationStore } from './investigation_store'; export type RenderableInvestigateWidget = InvestigateWidget & { loading: boolean; element: React.ReactNode; }; -export type RenderableInvestigationRevision = Omit<StatefulInvestigationRevision, 'items'> & { +export type RenderableInvestigation = Omit<StatefulInvestigation, 'items'> & { items: RenderableInvestigateWidget[]; }; -export type RenderableInvestigateTimeline = Omit<StatefulInvestigation, 'revisions'> & { - revisions: RenderableInvestigationRevision[]; -}; - export interface UseInvestigationApi { startNewInvestigation: (id: string) => void; loadInvestigation: (id: string) => void; - investigation?: Omit<StatefulInvestigation, 'revisions'>; - revision?: RenderableInvestigationRevision; - isAtLatestRevision: boolean; - isAtEarliestRevision: boolean; - setItemPositions: ( - positions: Array<{ id: string; columns: number; rows: number }> - ) => Promise<void>; - setItemTitle: (id: string, title: string) => Promise<void>; - updateItem: ( - id: string, - cb: (item: InvestigateWidget) => Promise<InvestigateWidget> - ) => Promise<void>; + investigations: Investigation[]; + investigation?: StatefulInvestigation; + renderableInvestigation?: RenderableInvestigation; copyItem: (id: string) => Promise<void>; deleteItem: (id: string) => Promise<void>; addItem: (options: InvestigateWidgetCreate) => Promise<void>; - lockItem: (id: string) => Promise<void>; - unlockItem: (id: string) => Promise<void>; - setItemParameters: ( - id: string, - parameters: GlobalWidgetParameters & Record<string, any> - ) => Promise<void>; setGlobalParameters: (parameters: GlobalWidgetParameters) => Promise<void>; - blocks: WorkflowBlock[]; - setRevision: (revisionId: string) => void; - gotoPreviousRevision: () => Promise<void>; - gotoNextRevision: () => Promise<void>; setTitle: (title: string) => Promise<void>; - investigations: Investigation[]; deleteInvestigation: (id: string) => Promise<void>; } @@ -98,7 +65,6 @@ function useInvestigationWithoutContext({ user, id: v4(), globalWidgetParameters: { - filters: [], timeRange: { from, to, @@ -109,22 +75,10 @@ function useInvestigationWithoutContext({ ); const investigation$ = investigationStore.asObservable(); - const investigation = useObservable(investigation$)?.investigation; - const currentRevision = useMemo(() => { - return investigation?.revisions.find((revision) => revision.id === investigation.revision); - }, [investigation?.revision, investigation?.revisions]); - - const isAtEarliestRevision = investigation?.revisions[0].id === currentRevision?.id; - - const isAtLatestRevision = last(investigation?.revisions)?.id === currentRevision?.id; - - const [blocksById, setBlocksById] = useState<Record<string, WorkflowBlock[]>>({}); - const deleteItem = useCallback( async (id: string) => { - setBlocksById((prevBlocks) => omit(prevBlocks, id)); return investigationStore.deleteItem(id); }, [investigationStore] @@ -141,7 +95,7 @@ function useInvestigationWithoutContext({ const unusedComponentIds = Object.keys(widgetComponentsById); const nextItemsWithContext = - currentRevision?.items.map((item) => { + investigation?.items.map((item) => { let Component = widgetComponentsById.current[item.id]; if (!Component) { const id = item.id; @@ -149,21 +103,6 @@ function useInvestigationWithoutContext({ onWidgetAdd: async (create) => { return addItemRef.current(create); }, - blocks: { - publish: (nextBlocks) => { - const nextIds = nextBlocks.map((block) => block.id); - setBlocksById((prevBlocksById) => ({ - ...prevBlocksById, - [id]: (prevBlocksById[id] ?? []).concat(nextBlocks), - })); - return () => { - setBlocksById((prevBlocksById) => ({ - ...prevBlocksById, - [id]: (prevBlocksById[id] ?? []).filter((block) => !nextIds.includes(block.id)), - })); - }; - }, - }, }; const onDelete = () => { @@ -179,7 +118,6 @@ function useInvestigationWithoutContext({ <InvestigateWidgetApiContextProvider value={api}> {widgetDefinition ? widgetDefinition.render({ - blocks: api.blocks, onWidgetAdd: api.onWidgetAdd, onDelete, widget: props.widget, @@ -203,15 +141,13 @@ function useInvestigationWithoutContext({ }); return nextItemsWithContext; - }, [currentRevision?.items, widgetDefinitions, investigationStore]); + }, [investigation?.items, widgetDefinitions, investigationStore]); const addItem = useCallback( async (widget: InvestigateWidgetCreate) => { try { const id = v4(); - setBlocksById((prevBlocksById) => ({ ...prevBlocksById, [id]: [] })); - await investigationStore.addItem(id, widget); } catch (error) { notifications.showErrorDialog({ @@ -228,10 +164,10 @@ function useInvestigationWithoutContext({ const addItemRef = useRef(addItem); addItemRef.current = addItem; - const renderableRevision = useMemo(() => { - return currentRevision + const renderableInvestigation = useMemo(() => { + return investigation ? { - ...currentRevision, + ...investigation, items: itemsWithContext.map((item) => { const { Component, ...rest } = item; return { @@ -241,17 +177,15 @@ function useInvestigationWithoutContext({ }), } : undefined; - }, [currentRevision, itemsWithContext]); + }, [investigation, itemsWithContext]); const startNewInvestigation = useCallback( async (id: string) => { const prevInvestigation = await investigationStore.getInvestigation(); - const lastRevision = last(prevInvestigation.revisions)!; - const createdInvestigationStore = createInvestigationStore({ investigation: createNewInvestigation({ - globalWidgetParameters: lastRevision.parameters, + globalWidgetParameters: prevInvestigation.parameters, user, id, }) as StatefulInvestigation, @@ -260,38 +194,11 @@ function useInvestigationWithoutContext({ }); setInvestigationStore(createdInvestigationStore); - - setBlocksById({}); }, [user, widgetDefinitions, investigationStore] ); - const setItemParameters = useCallback( - async (id: string, nextGlobalWidgetParameters: GlobalWidgetParameters) => { - return investigationStore.setItemParameters(id, nextGlobalWidgetParameters); - }, - [investigationStore] - ); - - const lastItemId = last(currentRevision?.items)?.id; - - const blocks = useMemo(() => { - return lastItemId && blocksById[lastItemId] ? blocksById[lastItemId] : []; - }, [blocksById, lastItemId]); - - const { - copyItem, - gotoNextRevision, - gotoPreviousRevision, - lockItem, - setGlobalParameters, - setItemPositions, - setItemTitle, - setRevision, - setTitle, - unlockItem, - updateItem, - } = investigationStore; + const { copyItem, setGlobalParameters, setTitle } = investigationStore; const { storedItem: investigations, setStoredItem: setInvestigations } = useLocalStorage< Investigation[] @@ -337,7 +244,7 @@ function useInvestigationWithoutContext({ } const subscription = investigation$.subscribe(({ investigation: investigationFromStore }) => { - const isEmpty = investigationFromStore.revisions.length === 1; + const isEmpty = investigationFromStore.items.length === 0; if (isEmpty) { return; @@ -345,14 +252,9 @@ function useInvestigationWithoutContext({ const toSerialize = { ...investigationFromStore, - revisions: investigationFromStore.revisions.map((revision) => { - return { - ...revision, - items: revision.items.map((item) => { - const { loading, ...rest } = item; - return rest; - }), - }; + items: investigationFromStore.items.map((item) => { + const { loading, ...rest } = item; + return rest; }), }; @@ -403,26 +305,14 @@ function useInvestigationWithoutContext({ return { addItem, - blocks, copyItem, deleteItem, - gotoNextRevision, - gotoPreviousRevision, investigation, - isAtEarliestRevision, - isAtLatestRevision, loadInvestigation, - lockItem, - revision: renderableRevision, + renderableInvestigation, setGlobalParameters, - setItemParameters, - setItemPositions, - setItemTitle, - setRevision, setTitle, startNewInvestigation, - unlockItem, - updateItem, investigations, deleteInvestigation, }; diff --git a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/investigation_store.ts b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/investigation_store.ts index 54281b6347ea1..8502079a4b228 100644 --- a/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/investigation_store.ts +++ b/x-pack/plugins/observability_solution/investigate/public/hooks/use_investigation/investigation_store.ts @@ -5,17 +5,15 @@ * 2.0. */ -import { BehaviorSubject, Observable } from 'rxjs'; import type { AuthenticatedUser } from '@kbn/security-plugin/common'; -import { v4 } from 'uuid'; import { MaybePromise } from '@kbn/utility-types'; -import { keyBy } from 'lodash'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { v4 } from 'uuid'; import { InvestigateWidget, mergePlainObjects } from '../../../common'; import { GlobalWidgetParameters, InvestigateWidgetCreate, Investigation, - InvestigationRevision, } from '../../../common/types'; import { WidgetDefinition } from '../../types'; @@ -23,37 +21,19 @@ export type StatefulInvestigateWidget = InvestigateWidget & { loading: boolean; }; -export type StatefulInvestigation = Omit<Investigation, 'revisions'> & { - revisions: StatefulInvestigationRevision[]; -}; - -export type StatefulInvestigationRevision = Omit<InvestigationRevision, 'items'> & { +export type StatefulInvestigation = Omit<Investigation, 'items'> & { items: StatefulInvestigateWidget[]; }; interface InvestigationStore { - setItemPositions: ( - positions: Array<{ id: string; columns: number; rows: number }> - ) => Promise<void>; copyItem: (id: string) => Promise<void>; deleteItem: (id: string) => Promise<void>; addItem: (id: string, item: InvestigateWidgetCreate) => Promise<void>; - updateItem: ( - id: string, - cb: (prevItem: InvestigateWidget) => Promise<InvestigateWidget> - ) => Promise<void>; - lockItem: (id: string) => Promise<void>; - unlockItem: (id: string) => Promise<void>; - setItemTitle: (id: string, title: string) => Promise<void>; - setRevision: (revisionId: string) => Promise<void>; - gotoPreviousRevision: () => Promise<void>; - gotoNextRevision: () => Promise<void>; asObservable: () => Observable<{ investigation: StatefulInvestigation; }>; getInvestigation: () => Promise<Readonly<StatefulInvestigation>>; setGlobalParameters: (globalWidgetParameters: GlobalWidgetParameters) => Promise<void>; - setItemParameters: (id: string, parameters: GlobalWidgetParameters) => Promise<void>; setTitle: (title: string) => Promise<void>; destroy: () => void; } @@ -81,11 +61,7 @@ async function regenerateItem({ throw new Error(`Definition for widget ${widget.type} not found`); } - const nextParameters = mergePlainObjects( - globalWidgetParameters, - widget.parameters, - widget.locked ? {} : globalWidgetParameters - ); + const nextParameters = mergePlainObjects(widget.parameters, globalWidgetParameters); const widgetData = await definition.generate({ parameters: nextParameters, @@ -117,12 +93,7 @@ export function createInvestigationStore({ const observable$ = new BehaviorSubject<{ investigation: StatefulInvestigation }>({ investigation: { ...investigation, - revisions: investigation.revisions.map((revision) => { - return { - ...revision, - items: revision.items.map((item) => ({ ...item, loading: false })), - }; - }), + items: investigation.items.map((item) => ({ ...item, loading: false })), }, }); @@ -132,137 +103,14 @@ export function createInvestigationStore({ observable$.next({ investigation: await cb(observable$.value.investigation) }); } - async function updateRevisionInPlace( - cb: (prevRevision: StatefulInvestigationRevision) => MaybePromise<StatefulInvestigationRevision> - ) { - return updateInvestigationInPlace(async (prevInvestigation) => { - const currentRevision = prevInvestigation.revisions.find((revision) => { - return revision.id === prevInvestigation.revision; - })!; - - const newRevision = await cb(currentRevision); - - return { - ...prevInvestigation, - revisions: prevInvestigation.revisions.map((revision) => { - return revision.id === currentRevision.id ? newRevision : revision; - }), - }; - }); - } - - async function nextRevision( - cb: (prevRevision: StatefulInvestigationRevision) => MaybePromise<StatefulInvestigationRevision> - ) { - return updateInvestigationInPlace(async (prevInvestigation) => { - const indexOfCurrentRevision = prevInvestigation.revisions.findIndex( - (revision) => revision.id === prevInvestigation.revision - ); - - const currentRevision = prevInvestigation.revisions[indexOfCurrentRevision]; - - const newRevision = { - ...(await cb(currentRevision)), - id: v4(), - }; - - return { - ...prevInvestigation, - revisions: prevInvestigation.revisions - .slice(0, indexOfCurrentRevision + 1) - .concat(newRevision) - .slice(-10), - revision: newRevision.id, - }; - }); - } - - async function updateItem( - itemId: string, - cb: (prevItem: InvestigateWidget) => MaybePromise<InvestigateWidget> - ) { - await updateRevisionInPlace(async (prevRevision) => { - const prevItem = prevRevision.items.find((item) => item.id === itemId); - if (!prevItem) { - throw new Error('Could not find item by id ' + itemId); - } - return { - ...prevRevision, - items: prevRevision.items.map((item) => { - if (item === prevItem) { - return { ...prevItem, loading: true }; - } - return item; - }), - }; - }); - - await nextRevision(async (prevRevision) => { - const prevItem = prevRevision.items.find((item) => item.id === itemId); - if (!prevItem) { - throw new Error('Could not find item by id ' + itemId); - } - const nextItem = await cb(prevItem); - return { - ...prevRevision, - items: prevRevision.items.map((item) => { - if (item === prevItem) { - return { ...nextItem, loading: false }; - } - return item; - }), - }; - }); - } - - const regenerateItemAndUpdateRevision = async ( - itemId: string, - partials: Partial<InvestigateWidget> - ) => { - await updateRevisionInPlace((prevRevision) => { - return { - ...prevRevision, - items: prevRevision.items.map((item) => { - if (item.id === itemId) { - return { ...item, loading: true, ...partials }; - } - return item; - }), - }; - }); - - await nextRevision(async (prevRevision) => { - return { - ...prevRevision, - items: await Promise.all( - prevRevision.items.map(async (item) => { - if (item.id === itemId) { - return { - ...(await regenerateItem({ - user, - globalWidgetParameters: prevRevision.parameters, - signal: controller.signal, - widget: item, - widgetDefinitions, - })), - loading: false, - }; - } - return item; - }) - ), - }; - }); - }; - const asObservable = observable$.asObservable(); return { addItem: (itemId, item) => { - return nextRevision(async (prevRevision) => { + return updateInvestigationInPlace(async (prevInvestigation) => { return { - ...prevRevision, - items: prevRevision.items.concat({ + ...prevInvestigation, + items: prevInvestigation.items.concat({ ...(await regenerateItem({ user, widgetDefinitions, @@ -271,25 +119,22 @@ export function createInvestigationStore({ ...item, id: itemId, }, - globalWidgetParameters: prevRevision.parameters, + globalWidgetParameters: prevInvestigation.parameters, })), loading: false, }), }; }); }, - updateItem: async (itemId, cb) => { - return updateItem(itemId, cb); - }, copyItem: (itemId) => { - return nextRevision((prevRevision) => { - const itemToCopy = prevRevision.items.find((item) => item.id === itemId); + return updateInvestigationInPlace((prevInvestigation) => { + const itemToCopy = prevInvestigation.items.find((item) => item.id === itemId); if (!itemToCopy) { throw new Error('Cannot find item for id ' + itemId); } return { - ...prevRevision, - items: prevRevision.items.concat({ + ...prevInvestigation, + items: prevInvestigation.items.concat({ ...itemToCopy, id: v4(), }), @@ -297,72 +142,17 @@ export function createInvestigationStore({ }); }, deleteItem: (itemId) => { - return nextRevision((prevRevision) => { - const itemToDelete = prevRevision.items.find((item) => item.id === itemId); - if (!itemToDelete) { - return prevRevision; - } - return { - ...prevRevision, - items: prevRevision.items.filter((itemAtIndex) => itemAtIndex.id !== itemToDelete.id), - }; - }); - }, - setItemPositions: (positions) => { - return nextRevision((prevRevision) => { - const positionsById = keyBy(positions, (position) => position.id); - return { - ...prevRevision, - items: prevRevision.items.map((item) => { - const position = positionsById[item.id]; - - return { - ...item, - ...position, - }; - }), - }; - }); - }, - setRevision: (revision) => { - return updateInvestigationInPlace((prevInvestigation) => { - return { - ...prevInvestigation, - revision, - }; - }); - }, - gotoPreviousRevision: () => { return updateInvestigationInPlace((prevInvestigation) => { - const indexOfCurrentRevision = prevInvestigation.revisions.findIndex( - (revision) => revision.id === prevInvestigation.revision - ); - - const targetRevision = prevInvestigation.revisions[indexOfCurrentRevision - 1]; - if (!targetRevision) { - throw new Error('Could not find previous revision'); - } - - return { - ...prevInvestigation, - revision: targetRevision.id, - }; - }); - }, - gotoNextRevision: () => { - return updateInvestigationInPlace((prevInvestigation) => { - const indexOfCurrentRevision = prevInvestigation.revisions.findIndex( - (revision) => revision.id === prevInvestigation.revision - ); - - const targetRevision = prevInvestigation.revisions[indexOfCurrentRevision + 1]; - if (!targetRevision) { - throw new Error('Could not find previous revision'); + const itemToDelete = prevInvestigation.items.find((item) => item.id === itemId); + if (!itemToDelete) { + return prevInvestigation; } return { ...prevInvestigation, - revision: targetRevision.id, + items: prevInvestigation.items.filter( + (itemAtIndex) => itemAtIndex.id !== itemToDelete.id + ), }; }); }, @@ -372,54 +162,40 @@ export function createInvestigationStore({ return controller.abort(); }, setGlobalParameters: async (parameters) => { - await updateRevisionInPlace((prevRevision) => { + await updateInvestigationInPlace((prevInvestigation) => { return { - ...prevRevision, - items: prevRevision.items.map((item) => { - return { ...item, loading: !item.locked }; + ...prevInvestigation, + items: prevInvestigation.items.map((item) => { + return { ...item, loading: true }; }), }; }); - await nextRevision(async (prevRevision) => { + await updateInvestigationInPlace(async (prevInvestigation) => { return { - ...prevRevision, + ...prevInvestigation, parameters, items: await Promise.all( - prevRevision.items.map(async (item) => { - return item.locked - ? item - : { - ...(await regenerateItem({ - widget: item, - globalWidgetParameters: parameters, - signal: controller.signal, - user, - widgetDefinitions, - })), - loading: false, - }; + prevInvestigation.items.map(async (item) => { + return { + ...(await regenerateItem({ + widget: item, + globalWidgetParameters: parameters, + signal: controller.signal, + user, + widgetDefinitions, + })), + loading: false, + }; }) ), }; }); }, - setItemTitle: async (itemId, title) => { - return updateItem(itemId, (prev) => ({ ...prev, title })); - }, - lockItem: async (itemId) => { - return updateItem(itemId, (prev) => ({ ...prev, locked: true })); - }, - unlockItem: async (itemId) => { - await regenerateItemAndUpdateRevision(itemId, { locked: false }); - }, setTitle: async (title: string) => { - return nextRevision((prevRevision) => { - return { ...prevRevision, title }; + return updateInvestigationInPlace((prevInvestigation) => { + return { ...prevInvestigation, title }; }); }, - setItemParameters: async (itemId, nextParameters) => { - await regenerateItemAndUpdateRevision(itemId, { parameters: nextParameters }); - }, }; } diff --git a/x-pack/plugins/observability_solution/investigate/public/index.ts b/x-pack/plugins/observability_solution/investigate/public/index.ts index b830aebc89947..8d296a321c94d 100644 --- a/x-pack/plugins/observability_solution/investigate/public/index.ts +++ b/x-pack/plugins/observability_solution/investigate/public/index.ts @@ -21,12 +21,10 @@ export type { InvestigatePublicSetup, InvestigatePublicStart, OnWidgetAdd, Widge export { type Investigation, - type InvestigationRevision, type InvestigateWidget, type InvestigateWidgetCreate, InvestigateWidgetColumnSpan, type GlobalWidgetParameters, - type WorkflowBlock, } from '../common/types'; export { mergePlainObjects } from '../common/utils/merge_plain_objects'; diff --git a/x-pack/plugins/observability_solution/investigate/public/types.ts b/x-pack/plugins/observability_solution/investigate/public/types.ts index fc6ca0376d20f..8951781be99f2 100644 --- a/x-pack/plugins/observability_solution/investigate/public/types.ts +++ b/x-pack/plugins/observability_solution/investigate/public/types.ts @@ -9,7 +9,7 @@ import type { FromSchema } from 'json-schema-to-ts'; import type { CompatibleJSONSchema } from '@kbn/observability-ai-assistant-plugin/public'; import type { AuthenticatedUser } from '@kbn/core/public'; -import type { InvestigateWidget, WorkflowBlock } from '../common'; +import type { InvestigateWidget } from '../common'; import type { GlobalWidgetParameters, InvestigateWidgetCreate } from '../common/types'; import type { UseInvestigationApi } from './hooks/use_investigation'; import type { UseInvestigateWidgetApi } from './hooks/use_investigate_widget'; @@ -22,14 +22,9 @@ export enum ChromeOption { export type OnWidgetAdd = (create: InvestigateWidgetCreate) => Promise<void>; -type UnregisterFunction = () => void; - export interface WidgetRenderAPI { onDelete: () => void; onWidgetAdd: OnWidgetAdd; - blocks: { - publish: (blocks: WorkflowBlock[]) => UnregisterFunction; - }; } type WidgetRenderOptions<TInvestigateWidget extends InvestigateWidget> = { diff --git a/x-pack/plugins/observability_solution/investigate/public/util/get_es_filters_from_global_parameters.ts b/x-pack/plugins/observability_solution/investigate/public/util/get_es_filters_from_global_parameters.ts index 8d8592bf09a9a..10d3c76a86d03 100644 --- a/x-pack/plugins/observability_solution/investigate/public/util/get_es_filters_from_global_parameters.ts +++ b/x-pack/plugins/observability_solution/investigate/public/util/get_es_filters_from_global_parameters.ts @@ -8,11 +8,10 @@ import { type BoolQuery, buildEsQuery } from '@kbn/es-query'; import type { GlobalWidgetParameters } from '../../common/types'; -export function getEsFilterFromGlobalParameters({ - filters, - timeRange, -}: Partial<GlobalWidgetParameters>): { bool: BoolQuery } { - const esFilter = buildEsQuery(undefined, [], filters ?? []); +export function getEsFilterFromGlobalParameters({ timeRange }: Partial<GlobalWidgetParameters>): { + bool: BoolQuery; +} { + const esFilter = buildEsQuery(undefined, [], []); if (timeRange) { esFilter.bool.filter.push({ diff --git a/x-pack/plugins/observability_solution/investigate_app/common/schema/create.ts b/x-pack/plugins/observability_solution/investigate_app/common/schema/create.ts new file mode 100644 index 0000000000000..050fa67d5cbaf --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/common/schema/create.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 * as t from 'io-ts'; +import { investigationResponseSchema } from './investigation'; + +const createInvestigationParamsSchema = t.type({ + body: t.type({ + id: t.string, + title: t.string, + parameters: t.type({ + timeRange: t.type({ from: t.number, to: t.number }), + }), + }), +}); + +const createInvestigationResponseSchema = investigationResponseSchema; + +type CreateInvestigationInput = t.OutputOf<typeof createInvestigationParamsSchema.props.body>; // Raw payload sent by the frontend +type CreateInvestigationParams = t.TypeOf<typeof createInvestigationParamsSchema.props.body>; // Parsed payload used by the backend +type CreateInvestigationResponse = t.OutputOf<typeof createInvestigationResponseSchema>; // Raw response sent to the frontend + +export { createInvestigationParamsSchema, createInvestigationResponseSchema }; +export type { CreateInvestigationInput, CreateInvestigationParams, CreateInvestigationResponse }; diff --git a/x-pack/plugins/observability_solution/investigate_app/common/schema/find.ts b/x-pack/plugins/observability_solution/investigate_app/common/schema/find.ts new file mode 100644 index 0000000000000..dc76a39fce679 --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/common/schema/find.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as t from 'io-ts'; +import { investigationResponseSchema } from './investigation'; + +const findInvestigationsParamsSchema = t.partial({ + query: t.partial({ + page: t.string, + perPage: t.string, + }), +}); + +const findInvestigationsResponseSchema = t.type({ + page: t.number, + perPage: t.number, + total: t.number, + results: t.array(investigationResponseSchema), +}); + +type FindInvestigationsParams = t.TypeOf<typeof findInvestigationsParamsSchema.props.query>; // Parsed payload used by the backend +type FindInvestigationsResponse = t.OutputOf<typeof findInvestigationsResponseSchema>; // Raw response sent to the frontend + +export { findInvestigationsParamsSchema, findInvestigationsResponseSchema }; +export type { FindInvestigationsParams, FindInvestigationsResponse }; diff --git a/x-pack/plugins/observability_solution/investigate_app/common/schema/get.ts b/x-pack/plugins/observability_solution/investigate_app/common/schema/get.ts new file mode 100644 index 0000000000000..b30fb9f61cff8 --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/common/schema/get.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 * as t from 'io-ts'; +import { investigationResponseSchema } from './investigation'; + +const getInvestigationParamsSchema = t.type({ + path: t.type({ + id: t.string, + }), +}); + +const getInvestigationResponseSchema = investigationResponseSchema; + +type GetInvestigationParams = t.TypeOf<typeof getInvestigationParamsSchema.props.path>; // Parsed payload used by the backend +type GetInvestigationResponse = t.OutputOf<typeof getInvestigationResponseSchema>; // Raw response sent to the frontend + +export { getInvestigationParamsSchema, getInvestigationResponseSchema }; +export type { GetInvestigationParams, GetInvestigationResponse }; diff --git a/x-pack/plugins/observability_solution/investigate_app/common/schema/investigation.ts b/x-pack/plugins/observability_solution/investigate_app/common/schema/investigation.ts new file mode 100644 index 0000000000000..3046a5c4c6d8a --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/common/schema/investigation.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 * as t from 'io-ts'; + +const investigationResponseSchema = t.type({ + id: t.string, + title: t.string, + createdAt: t.number, + createdBy: t.string, + parameters: t.type({ + timeRange: t.type({ from: t.number, to: t.number }), + }), +}); + +type InvestigationResponse = t.OutputOf<typeof investigationResponseSchema>; + +export { investigationResponseSchema }; +export type { InvestigationResponse }; diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/add_note_ui/index.stories.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/add_note_ui/index.stories.tsx index edb714c6d388e..0db8b3b67ee35 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/add_note_ui/index.stories.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/add_note_ui/index.stories.tsx @@ -34,7 +34,6 @@ const defaultStory: Story = { username: 'johndoe', full_name: 'John Doe', }, - filters: [], timeRange: { from: moment().subtract(15, 'minutes').toISOString(), to: moment().toISOString(), diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/esql_widget_preview.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/esql_widget_preview.tsx index 2bd84639e67dc..a661e6fdc8eae 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/esql_widget_preview.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/esql_widget_preview.tsx @@ -52,7 +52,6 @@ function getWidgetFromSuggestion({ }, columns: makeItWide ? InvestigateWidgetColumnSpan.Four : InvestigateWidgetColumnSpan.One, rows, - locked: false, }); } @@ -79,7 +78,6 @@ function PreviewContainer({ children }: { children: React.ReactNode }) { export function EsqlWidgetPreview({ esqlQuery, onWidgetAdd, - filters, timeRange, }: { esqlQuery: string; @@ -91,10 +89,9 @@ export function EsqlWidgetPreview({ const filter = useMemo(() => { return getEsFilterFromOverrides({ - filters, timeRange, }); - }, [filters, timeRange]); + }, [timeRange]); const [selectedSuggestion, setSelectedSuggestion] = useState<Suggestion | undefined>(undefined); diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/index.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/index.tsx index 7d5424f9005d5..69f43ef515146 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/index.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/add_observation_ui/index.tsx @@ -21,7 +21,7 @@ const emptyPreview = css` padding: 36px 0px 36px 0px; `; -export function AddObservationUI({ onWidgetAdd, timeRange, filters }: Props) { +export function AddObservationUI({ onWidgetAdd, timeRange }: Props) { const [isOpen, setIsOpen] = React.useState(false); const [query, setQuery] = React.useState({ esql: '' }); @@ -111,7 +111,6 @@ export function AddObservationUI({ onWidgetAdd, timeRange, filters }: Props) { </EuiFlexGroup> ) : ( <EsqlWidgetPreview - filters={filters} esqlQuery={submittedQuery.esql} timeRange={timeRange} onWidgetAdd={(widget) => { diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.stories.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.stories.tsx index aff1eab514351..0f66c5403e172 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.stories.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.stories.tsx @@ -40,18 +40,11 @@ const defaultProps: Story = { return ( <div style={{ width: 800, height: 600 }}> <Component - faded={false} - locked={false} loading={false} onCopy={() => {}} onDelete={() => {}} - onLockToggle={() => {}} - onOverrideRemove={async () => {}} - onTitleChange={() => {}} - overrides={[]} title="My visualization" description="A long description" - onEditClick={() => {}} {...props} /> </div> diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.tsx index df418271fac22..1068d59ce304c 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/grid_item/index.tsx @@ -4,14 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { css } from '@emotion/css'; -import classNames from 'classnames'; import React from 'react'; -import { i18n } from '@kbn/i18n'; import { useTheme } from '../../hooks/use_theme'; import { InvestigateTextButton } from '../investigate_text_button'; -import { InvestigateWidgetGridItemOverride } from '../investigate_widget_grid'; export const GRID_ITEM_HEADER_HEIGHT = 40; @@ -20,16 +17,9 @@ interface GridItemProps { title: string; description: string; children: React.ReactNode; - locked: boolean; onCopy: () => void; - onTitleChange: (title: string) => void; onDelete: () => void; - onLockToggle: () => void; loading: boolean; - faded: boolean; - onOverrideRemove: (override: InvestigateWidgetGridItemOverride) => Promise<void>; - onEditClick: () => void; - overrides: InvestigateWidgetGridItemOverride[]; } const editTitleButtonClassName = `investigateGridItemTitleEditButton`; @@ -46,17 +36,6 @@ const titleItemClassName = css` } `; -const fadedClassName = css` - opacity: 0.5 !important; -`; - -const lockedControlClassName = css` - opacity: 0.9 !important; - &:hover { - opacity: 1 !important; - } -`; - const panelContainerClassName = css` overflow: clip; overflow-clip-margin: 20px; @@ -78,28 +57,14 @@ const headerClassName = css` height: ${GRID_ITEM_HEADER_HEIGHT}px; `; -const changeBadgeClassName = css` - max-width: 96px; - .euiText { - text-overflow: ellipsis; - overflow: hidden; - } -`; - export function GridItem({ id, title, description, children, - locked, - onLockToggle, onDelete, onCopy, loading, - faded, - overrides, - onOverrideRemove, - onEditClick, }: GridItemProps) { const theme = useTheme(); @@ -118,7 +83,7 @@ export function GridItem({ <EuiFlexGroup direction="column" gutterSize="none" - className={faded ? classNames(containerClassName, fadedClassName) : containerClassName} + className={containerClassName} alignItems="stretch" > <EuiFlexItem grow={false}> @@ -133,33 +98,6 @@ export function GridItem({ {title} </EuiText> </EuiFlexItem> - <EuiFlexItem grow={false}> - {overrides.length ? ( - <EuiFlexGroup direction="row" gutterSize="xs" justifyContent="flexStart"> - {overrides.map((override) => ( - <EuiFlexItem key={override.id} grow={false}> - <EuiBadge - color="primary" - className={changeBadgeClassName} - iconType="cross" - iconSide="right" - iconOnClick={() => { - onOverrideRemove(override); - }} - iconOnClickAriaLabel={i18n.translate( - 'xpack.investigateApp.gridItem.removeOverrideButtonAriaLabel', - { - defaultMessage: 'Remove filter', - } - )} - > - <EuiText size="xs">{override.label}</EuiText> - </EuiBadge> - </EuiFlexItem> - ))} - </EuiFlexGroup> - ) : null} - </EuiFlexItem> <EuiFlexItem grow={false} className="gridItemControls"> <EuiFlexGroup direction="row" @@ -185,26 +123,6 @@ export function GridItem({ disabled={loading} /> </EuiFlexItem> - <EuiFlexItem grow={false}> - <InvestigateTextButton - iconType="pencil" - onClick={() => { - onEditClick(); - }} - disabled={loading} - /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <InvestigateTextButton - iconType={locked ? 'lock' : 'lockOpen'} - className={locked ? lockedControlClassName : ''} - color={locked ? 'primary' : 'text'} - onClick={() => { - onLockToggle(); - }} - disabled={loading} - /> - </EuiFlexItem> </EuiFlexGroup> </EuiFlexItem> </EuiFlexGroup> diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_view/index.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_view/index.tsx index a562c3647fcd4..37ed996162ed2 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_view/index.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_view/index.tsx @@ -6,15 +6,13 @@ */ import datemath from '@elastic/datemath'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; -import type { InvestigateWidget, InvestigateWidgetCreate } from '@kbn/investigate-plugin/public'; -import { DATE_FORMAT_ID } from '@kbn/management-settings-ids'; +import type { InvestigateWidgetCreate } from '@kbn/investigate-plugin/public'; import { AuthenticatedUser } from '@kbn/security-plugin/common'; -import { keyBy, omit, pick } from 'lodash'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { keyBy, noop } from 'lodash'; +import React, { useEffect, useMemo, useRef } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { useDateRange } from '../../hooks/use_date_range'; import { useKibana } from '../../hooks/use_kibana'; -import { getOverridesFromGlobalParameters } from '../../utils/get_overrides_from_global_parameters'; import { AddNoteUI } from '../add_note_ui'; import { AddObservationUI } from '../add_observation_ui'; import { InvestigateSearchBar } from '../investigate_search_bar'; @@ -22,35 +20,26 @@ import { InvestigateWidgetGrid } from '../investigate_widget_grid'; function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { const { - core: { uiSettings }, dependencies: { start: { investigate }, }, } = useKibana(); const widgetDefinitions = useMemo(() => investigate.getWidgetDefinitions(), [investigate]); const [range, setRange] = useDateRange(); - const [searchBarFocused, setSearchBarFocused] = useState(false); const { addItem, - setItemPositions, - setItemTitle, copyItem, deleteItem, investigation, - lockItem, - setItemParameters, setGlobalParameters, - unlockItem, - revision, + renderableInvestigation, } = investigate.useInvestigation({ user, from: range.start.toISOString(), to: range.end.toISOString(), }); - const [_editingItem, setEditingItem] = useState<InvestigateWidget | undefined>(undefined); - const createWidget = (widgetCreate: InvestigateWidgetCreate) => { return addItem(widgetCreate); }; @@ -58,31 +47,21 @@ function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { const createWidgetRef = useRef(createWidget); createWidgetRef.current = createWidget; - useEffect(() => { - const itemIds = revision?.items.map((item) => item.id) ?? []; - setEditingItem((prevEditingItem) => { - if (prevEditingItem && !itemIds.includes(prevEditingItem.id)) { - return undefined; - } - return prevEditingItem; - }); - }, [revision]); - useEffect(() => { if ( - revision?.parameters.timeRange.from && - revision?.parameters.timeRange.to && - range.start.toISOString() !== revision.parameters.timeRange.from && - range.end.toISOString() !== revision.parameters.timeRange.to + renderableInvestigation?.parameters.timeRange.from && + renderableInvestigation?.parameters.timeRange.to && + range.start.toISOString() !== renderableInvestigation.parameters.timeRange.from && + range.end.toISOString() !== renderableInvestigation.parameters.timeRange.to ) { setRange({ - from: revision.parameters.timeRange.from, - to: revision.parameters.timeRange.to, + from: renderableInvestigation.parameters.timeRange.from, + to: renderableInvestigation.parameters.timeRange.to, }); } }, [ - revision?.parameters.timeRange.from, - revision?.parameters.timeRange.to, + renderableInvestigation?.parameters.timeRange.from, + renderableInvestigation?.parameters.timeRange.to, range.start, range.end, setRange, @@ -91,7 +70,7 @@ function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { const gridItems = useMemo(() => { const widgetDefinitionsByType = keyBy(widgetDefinitions, 'type'); - return revision?.items.map((item) => { + return renderableInvestigation?.items.map((item) => { const definitionForType = widgetDefinitionsByType[item.type]; return ( @@ -103,21 +82,13 @@ function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { columns: item.columns, rows: item.rows, chrome: definitionForType.chrome, - locked: item.locked, loading: item.loading, - overrides: item.locked - ? getOverridesFromGlobalParameters( - pick(item.parameters, 'filters', 'timeRange'), - revision.parameters, - uiSettings.get<string>(DATE_FORMAT_ID) ?? 'Browser' - ) - : [], } ?? [] ); }); - }, [revision, widgetDefinitions, uiSettings]); + }, [renderableInvestigation, widgetDefinitions]); - if (!investigation || !revision || !gridItems) { + if (!investigation || !renderableInvestigation || !gridItems) { return <EuiLoadingSpinner />; } @@ -136,18 +107,12 @@ function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { to: datemath.parse(dateRange.to)!.toISOString(), }; await setGlobalParameters({ - ...revision.parameters, + ...renderableInvestigation.parameters, timeRange: nextDateRange, }); setRange(nextDateRange); }} - onFocus={() => { - setSearchBarFocused(true); - }} - onBlur={() => { - setSearchBarFocused(false); - }} /> </EuiFlexItem> @@ -155,16 +120,7 @@ function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { <InvestigateWidgetGrid items={gridItems} onItemsChange={async (nextGridItems) => { - return setItemPositions( - nextGridItems.map((gridItem) => ({ - columns: gridItem.columns, - rows: gridItem.rows, - id: gridItem.id, - })) - ); - }} - onItemTitleChange={async (item, title) => { - return setItemTitle(item.id, title); + noop(); }} onItemCopy={async (copiedItem) => { return copyItem(copiedItem.id); @@ -172,30 +128,12 @@ function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { onItemDelete={async (deletedItem) => { return deleteItem(deletedItem.id); }} - onItemLockToggle={async (toggledItem) => { - return toggledItem.locked ? unlockItem(toggledItem.id) : lockItem(toggledItem.id); - }} - fadeLockedItems={searchBarFocused} - onItemOverrideRemove={async (updatedItem, override) => { - // TODO: remove filters - const itemToUpdate = revision.items.find((item) => item.id === updatedItem.id); - if (itemToUpdate) { - return setItemParameters(updatedItem.id, { - ...revision.parameters, - ...omit(itemToUpdate.parameters, override.id), - }); - } - }} - onItemEditClick={(itemToEdit) => { - setEditingItem(revision.items.find((item) => item.id === itemToEdit.id)); - }} /> </EuiFlexItem> </EuiFlexGroup> <AddObservationUI - filters={revision.parameters.filters} - timeRange={revision.parameters.timeRange} + timeRange={renderableInvestigation.parameters.timeRange} onWidgetAdd={(widget) => { return createWidgetRef.current(widget); }} @@ -206,8 +144,7 @@ function InvestigateViewWithUser({ user }: { user: AuthenticatedUser }) { <EuiFlexItem grow={2}> <AddNoteUI user={user} - filters={revision.parameters.filters} - timeRange={revision.parameters.timeRange} + timeRange={renderableInvestigation.parameters.timeRange} onWidgetAdd={(widget) => { return createWidgetRef.current(widget); }} diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.stories.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.stories.tsx index f4436051d76c2..5962a55fa22f0 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.stories.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.stories.tsx @@ -62,7 +62,6 @@ function createItem<T extends Partial<InvestigateWidgetGridItem>>(overrides: T) columns: 4, rows: 2, description: '', - locked: false, loading: false, overrides: [], }; @@ -96,7 +95,6 @@ export const InvestigateWidgetGridStory: ComponentStoryObj<typeof Component> = { ), columns: 4, rows: 12, - locked: true, }), createItem({ title: '5', diff --git a/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.tsx b/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.tsx index eea40cdca391d..053433b83383e 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/components/investigate_widget_grid/index.tsx @@ -12,9 +12,9 @@ import React, { useCallback, useMemo, useRef } from 'react'; import { ItemCallback, Layout, Responsive, WidthProvider } from 'react-grid-layout'; import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; -import { EuiBreakpoint, EUI_BREAKPOINTS, useBreakpoints } from '../../hooks/use_breakpoints'; +import { EUI_BREAKPOINTS, EuiBreakpoint, useBreakpoints } from '../../hooks/use_breakpoints'; import { useTheme } from '../../hooks/use_theme'; -import { GridItem, GRID_ITEM_HEADER_HEIGHT } from '../grid_item'; +import { GRID_ITEM_HEADER_HEIGHT, GridItem } from '../grid_item'; import './styles.scss'; const gridContainerClassName = css` @@ -36,11 +36,6 @@ interface GridSection { type Section = SingleComponentSection | GridSection; -export interface InvestigateWidgetGridItemOverride { - id: string; - label: React.ReactNode; -} - export interface InvestigateWidgetGridItem { title: string; description: string; @@ -48,10 +43,8 @@ export interface InvestigateWidgetGridItem { id: string; columns: number; rows: number; - locked: boolean; chrome?: ChromeOption; loading: boolean; - overrides: InvestigateWidgetGridItemOverride[]; } interface InvestigateWidgetGridProps { @@ -59,14 +52,6 @@ interface InvestigateWidgetGridProps { onItemsChange: (items: InvestigateWidgetGridItem[]) => Promise<void>; onItemCopy: (item: InvestigateWidgetGridItem) => Promise<void>; onItemDelete: (item: InvestigateWidgetGridItem) => Promise<void>; - onItemLockToggle: (item: InvestigateWidgetGridItem) => Promise<void>; - onItemOverrideRemove: ( - item: InvestigateWidgetGridItem, - override: InvestigateWidgetGridItemOverride - ) => Promise<void>; - onItemTitleChange: (item: InvestigateWidgetGridItem, title: string) => Promise<void>; - onItemEditClick: (item: InvestigateWidgetGridItem) => void; - fadeLockedItems: boolean; } const ROW_HEIGHT = 32; @@ -130,11 +115,6 @@ function GridSectionRenderer({ onItemsChange, onItemDelete, onItemCopy, - onItemLockToggle, - onItemOverrideRemove, - onItemTitleChange, - onItemEditClick, - fadeLockedItems, }: InvestigateWidgetGridProps) { const WithFixedWidth = useMemo(() => WidthProvider(Responsive), []); @@ -144,14 +124,9 @@ function GridSectionRenderer({ onItemsChange, onItemCopy, onItemDelete, - onItemLockToggle, - onItemOverrideRemove, - onItemTitleChange, - onItemEditClick, }; const itemCallbacksRef = useRef(callbacks); - itemCallbacksRef.current = callbacks; const { currentBreakpoint } = useBreakpoints(); @@ -167,34 +142,19 @@ function GridSectionRenderer({ id={item.id} title={item.title} description={item.description} - onTitleChange={(title) => { - return itemCallbacksRef.current.onItemTitleChange(item, title); - }} onCopy={() => { return itemCallbacksRef.current.onItemCopy(item); }} onDelete={() => { return itemCallbacksRef.current.onItemDelete(item); }} - locked={item.locked} - onLockToggle={() => { - itemCallbacksRef.current.onItemLockToggle(item); - }} - onOverrideRemove={(override) => { - return itemCallbacksRef.current.onItemOverrideRemove(item, override); - }} - onEditClick={() => { - return itemCallbacksRef.current.onItemEditClick(item); - }} - overrides={item.overrides} loading={item.loading} - faded={fadeLockedItems && item.locked} > {item.element} </GridItem> </div> )); - }, [items, fadeLockedItems]); + }, [items]); // react-grid calls `onLayoutChange` every time // `layouts` changes, except when on mount. So... @@ -273,11 +233,6 @@ export function InvestigateWidgetGrid({ onItemsChange, onItemDelete, onItemCopy, - onItemLockToggle, - fadeLockedItems, - onItemOverrideRemove, - onItemTitleChange, - onItemEditClick, }: InvestigateWidgetGridProps) { const sections = useMemo<Section[]>(() => { let currentGrid: GridSection = { items: [] }; @@ -317,9 +272,6 @@ export function InvestigateWidgetGrid({ onItemDelete={(deletedItem) => { return onItemDelete(deletedItem); }} - onItemLockToggle={(toggledItem) => { - return onItemLockToggle(toggledItem); - }} onItemsChange={(itemsInSection) => { const nextItems = sections.flatMap((sectionAtIndex) => { if ('item' in sectionAtIndex) { @@ -333,16 +285,6 @@ export function InvestigateWidgetGrid({ return onItemsChange(nextItems); }} - onItemOverrideRemove={(item, override) => { - return onItemOverrideRemove(item, override); - }} - onItemTitleChange={(item, title) => { - return onItemTitleChange(item, title); - }} - onItemEditClick={(item) => { - return onItemEditClick(item); - }} - fadeLockedItems={fadeLockedItems} /> </EuiFlexItem> ); @@ -356,28 +298,13 @@ export function InvestigateWidgetGrid({ id={section.item.id} title={section.item.title} description={section.item.description} - faded={section.item.locked && fadeLockedItems} loading={section.item.loading} - locked={section.item.locked} - overrides={section.item.overrides} onCopy={() => { return onItemCopy(section.item); }} onDelete={() => { return onItemDelete(section.item); }} - onOverrideRemove={(override) => { - return onItemOverrideRemove(section.item, override); - }} - onTitleChange={(nextTitle) => { - return onItemTitleChange(section.item, nextTitle); - }} - onLockToggle={() => { - return onItemLockToggle(section.item); - }} - onEditClick={() => { - return onItemEditClick(section.item); - }} > {section.item.element} </GridItem> diff --git a/x-pack/plugins/observability_solution/investigate_app/public/utils/get_es_filter_from_overrides.ts b/x-pack/plugins/observability_solution/investigate_app/public/utils/get_es_filter_from_overrides.ts index ccc385701fb0f..18b28770772eb 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/utils/get_es_filter_from_overrides.ts +++ b/x-pack/plugins/observability_solution/investigate_app/public/utils/get_es_filter_from_overrides.ts @@ -5,19 +5,17 @@ * 2.0. */ -import { type BoolQuery, buildEsQuery, type Filter } from '@kbn/es-query'; +import { buildEsQuery, type BoolQuery } from '@kbn/es-query'; export function getEsFilterFromOverrides({ - filters, timeRange, }: { - filters?: Filter[]; timeRange?: { from: string; to: string; }; }): { bool: BoolQuery } { - const esFilter = buildEsQuery(undefined, [], filters ?? []); + const esFilter = buildEsQuery(undefined, [], []); if (timeRange) { esFilter.bool.filter.push({ diff --git a/x-pack/plugins/observability_solution/investigate_app/public/utils/get_overrides_from_global_parameters.tsx b/x-pack/plugins/observability_solution/investigate_app/public/utils/get_overrides_from_global_parameters.tsx deleted file mode 100644 index 8da9daf040e36..0000000000000 --- a/x-pack/plugins/observability_solution/investigate_app/public/utils/get_overrides_from_global_parameters.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 { isEqual } from 'lodash'; -import type { GlobalWidgetParameters } from '@kbn/investigate-plugin/public'; -import type { Filter } from '@kbn/es-query'; -import objectHash from 'object-hash'; -import { i18n } from '@kbn/i18n'; -import { PrettyDuration } from '@elastic/eui'; -import type { InvestigateWidgetGridItemOverride } from '../components/investigate_widget_grid'; - -enum OverrideType { - timeRange = 'timeRange', - filters = 'filters', -} - -function getIdForFilter(filter: Filter) { - return objectHash({ meta: filter.meta, query: filter.query }); -} - -function getLabelForFilter(filter: Filter) { - return ( - filter.meta.alias ?? - filter.meta.key ?? - JSON.stringify({ meta: filter.meta, query: filter.query }) - ); -} - -export function getOverridesFromGlobalParameters( - itemParameters: GlobalWidgetParameters, - globalParameters: GlobalWidgetParameters, - uiSettingsDateFormat: string -) { - const overrides: InvestigateWidgetGridItemOverride[] = []; - - if (!isEqual(itemParameters.timeRange, globalParameters.timeRange)) { - overrides.push({ - id: OverrideType.timeRange, - label: ( - <PrettyDuration - timeFrom={itemParameters.timeRange.from} - timeTo={itemParameters.timeRange.to} - dateFormat={uiSettingsDateFormat} - /> - ), - }); - } - - if (!isEqual(itemParameters.filters, globalParameters.filters)) { - if (!itemParameters.filters.length) { - overrides.push({ - id: OverrideType.filters, - label: i18n.translate('xpack.investigateApp.overrides.noFilters', { - defaultMessage: 'No filters', - }), - }); - } - - itemParameters.filters.forEach((filter) => { - overrides.push({ - id: `${OverrideType.filters}_${getIdForFilter(filter)}`, - label: getLabelForFilter(filter), - }); - }); - } - - return overrides; -} diff --git a/x-pack/plugins/observability_solution/investigate_app/public/widgets/embeddable_widget/register_embeddable_widget.tsx b/x-pack/plugins/observability_solution/investigate_app/public/widgets/embeddable_widget/register_embeddable_widget.tsx index e4a1ea7e3396d..779fb9c5301b9 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/widgets/embeddable_widget/register_embeddable_widget.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/widgets/embeddable_widget/register_embeddable_widget.tsx @@ -28,17 +28,16 @@ type Props = EmbeddableWidgetParameters & GlobalWidgetParameters; type ParentApi = ReturnType<React.ComponentProps<typeof ReactEmbeddableRenderer>['getParentApi']>; -function ReactEmbeddable({ type, config, filters, timeRange: { from, to }, savedObjectId }: Props) { +function ReactEmbeddable({ type, config, timeRange: { from, to }, savedObjectId }: Props) { const configWithOverrides = useMemo(() => { return { ...config, - filters, timeRange: { from, to, }, }; - }, [config, filters, from, to]); + }, [config, from, to]); const configWithOverridesRef = useRef(configWithOverrides); @@ -66,13 +65,7 @@ function ReactEmbeddable({ type, config, filters, timeRange: { from, to }, saved ); } -function LegacyEmbeddable({ - type, - config, - filters, - timeRange: { from, to }, - savedObjectId, -}: Props) { +function LegacyEmbeddable({ type, config, timeRange: { from, to }, savedObjectId }: Props) { const { dependencies: { start: { embeddable }, @@ -95,7 +88,6 @@ function LegacyEmbeddable({ const configWithOverrides = { ...configWithId, - filters, timeRange: { from, to, @@ -109,7 +101,7 @@ function LegacyEmbeddable({ const instance = await factory.create(configWithOverrides); return instance; - }, [type, savedObjectId, config, from, to, embeddable, filters]); + }, [type, savedObjectId, config, from, to, embeddable]); const embeddableInstance = embeddableInstanceAsync.value; @@ -193,7 +185,6 @@ export function registerEmbeddableWidget({ registerWidget }: RegisterWidgetOptio config: widget.parameters.config, savedObjectId: widget.parameters.savedObjectId, timeRange: widget.parameters.timeRange, - filters: widget.parameters.filters, query: widget.parameters.query, }; diff --git a/x-pack/plugins/observability_solution/investigate_app/public/widgets/esql_widget/register_esql_widget.tsx b/x-pack/plugins/observability_solution/investigate_app/public/widgets/esql_widget/register_esql_widget.tsx index ea990d4e4ac4c..14d18302ae516 100644 --- a/x-pack/plugins/observability_solution/investigate_app/public/widgets/esql_widget/register_esql_widget.tsx +++ b/x-pack/plugins/observability_solution/investigate_app/public/widgets/esql_widget/register_esql_widget.tsx @@ -82,7 +82,7 @@ export function EsqlWidget({ const timestampColumn = datatable.columns.find((column) => column.name === '@timestamp'); const messageColumn = datatable.columns.find((column) => column.name === 'message'); - if (datatable.columns.length > 100 && timestampColumn && messageColumn) { + if (datatable.columns.length > 20 && timestampColumn && messageColumn) { const hasDataForBothColumns = datatable.rows.every((row) => { const timestampValue = row['@timestamp']; const messageValue = row.message; @@ -183,6 +183,7 @@ export function EsqlWidget({ query={memoizedQueryObject} flyoutType="overlay" initialColumns={initialColumns} + initialRowHeight={1} /> </EuiFlexItem> </EuiFlexGroup> @@ -217,7 +218,6 @@ export function registerEsqlWidget({ async ({ parameters, signal }) => { const { esql: esqlQuery, - filters, timeRange, suggestion: suggestionFromParameters, } = parameters as EsqlWidgetParameters & GlobalWidgetParameters; @@ -226,7 +226,6 @@ export function registerEsqlWidget({ const esFilters = [ getEsFilterFromOverrides({ - filters, timeRange, }), ]; @@ -265,7 +264,7 @@ export function registerEsqlWidget({ dateHistogram: dateHistoResponse, }; }, - ({ widget, blocks }) => { + ({ widget }) => { const { main: { dataView, columns, values, suggestion }, dateHistogram, diff --git a/x-pack/plugins/observability_solution/investigate_app/server/models/investigation.ts b/x-pack/plugins/observability_solution/investigate_app/server/models/investigation.ts new file mode 100644 index 0000000000000..f079d922d1a9d --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/server/models/investigation.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 * as t from 'io-ts'; + +export const investigationSchema = t.type({ + id: t.string, + title: t.string, + createdAt: t.number, + createdBy: t.string, + parameters: t.type({ + timeRange: t.type({ from: t.number, to: t.number }), + }), +}); + +export type Investigation = t.TypeOf<typeof investigationSchema>; +export type StoredInvestigation = t.OutputOf<typeof investigationSchema>; diff --git a/x-pack/plugins/observability_solution/assets_data_access/server/services/register_services.ts b/x-pack/plugins/observability_solution/investigate_app/server/models/pagination.ts similarity index 56% rename from x-pack/plugins/observability_solution/assets_data_access/server/services/register_services.ts rename to x-pack/plugins/observability_solution/investigate_app/server/models/pagination.ts index dfac73c43df13..255614932fe5e 100644 --- a/x-pack/plugins/observability_solution/assets_data_access/server/services/register_services.ts +++ b/x-pack/plugins/observability_solution/investigate_app/server/models/pagination.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { Logger } from '@kbn/core/server'; - -export interface RegisterServicesParams { - logger: Logger; - deps: {}; +export interface Paginated<T> { + total: number; + page: number; + perPage: number; + results: T[]; } -export function registerServices(params: RegisterServicesParams) { - return {}; +export interface Pagination { + page: number; + perPage: number; } diff --git a/x-pack/plugins/observability_solution/investigate_app/server/plugin.ts b/x-pack/plugins/observability_solution/investigate_app/server/plugin.ts index 7b8a1772858ec..f1ee1cacd155b 100644 --- a/x-pack/plugins/observability_solution/investigate_app/server/plugin.ts +++ b/x-pack/plugins/observability_solution/investigate_app/server/plugin.ts @@ -17,6 +17,8 @@ import type { InvestigateAppSetupDependencies, InvestigateAppStartDependencies, } from './types'; +import { investigation } from './saved_objects/investigation'; +import { InvestigateAppConfig } from './config'; export class InvestigateAppPlugin implements @@ -28,9 +30,11 @@ export class InvestigateAppPlugin > { logger: Logger; + config: InvestigateAppConfig; constructor(context: PluginInitializerContext<ConfigSchema>) { this.logger = context.logger.get(); + this.config = context.config.get<InvestigateAppConfig>(); } setup( coreSetup: CoreSetup<InvestigateAppStartDependencies, InvestigateAppServerStart>, @@ -47,13 +51,17 @@ export class InvestigateAppPlugin }; }) as InvestigateAppRouteHandlerResources['plugins']; - registerServerRoutes({ - core: coreSetup, - logger: this.logger, - dependencies: { - plugins: routeHandlerPlugins, - }, - }); + if (this.config.enabled === true) { + coreSetup.savedObjects.registerType(investigation); + + registerServerRoutes({ + core: coreSetup, + logger: this.logger, + dependencies: { + plugins: routeHandlerPlugins, + }, + }); + } return {}; } diff --git a/x-pack/plugins/observability_solution/investigate_app/server/routes/get_global_investigate_app_server_route_repository.ts b/x-pack/plugins/observability_solution/investigate_app/server/routes/get_global_investigate_app_server_route_repository.ts index 63931f717385c..7565330316fed 100644 --- a/x-pack/plugins/observability_solution/investigate_app/server/routes/get_global_investigate_app_server_route_repository.ts +++ b/x-pack/plugins/observability_solution/investigate_app/server/routes/get_global_investigate_app_server_route_repository.ts @@ -5,8 +5,63 @@ * 2.0. */ +import { findInvestigationsParamsSchema } from '../../common/schema/find'; +import { createInvestigationParamsSchema } from '../../common/schema/create'; +import { createInvestigation } from '../services/create_investigation'; +import { investigationRepositoryFactory } from '../services/investigation_repository'; +import { createInvestigateAppServerRoute } from './create_investigate_app_server_route'; +import { findInvestigations } from '../services/find_investigations'; +import { getInvestigationParamsSchema } from '../../common/schema/get'; +import { getInvestigation } from '../services/get_investigation'; + +const createInvestigationRoute = createInvestigateAppServerRoute({ + endpoint: 'POST /api/observability/investigations 2023-10-31', + options: { + tags: [], + }, + params: createInvestigationParamsSchema, + handler: async (params) => { + const soClient = (await params.context.core).savedObjects.client; + const repository = investigationRepositoryFactory({ soClient, logger: params.logger }); + + return await createInvestigation(params.params.body, repository); + }, +}); + +const findInvestigationsRoute = createInvestigateAppServerRoute({ + endpoint: 'GET /api/observability/investigations 2023-10-31', + options: { + tags: [], + }, + params: findInvestigationsParamsSchema, + handler: async (params) => { + const soClient = (await params.context.core).savedObjects.client; + const repository = investigationRepositoryFactory({ soClient, logger: params.logger }); + + return await findInvestigations(params.params?.query ?? {}, repository); + }, +}); + +const getInvestigationRoute = createInvestigateAppServerRoute({ + endpoint: 'GET /api/observability/investigations/{id} 2023-10-31', + options: { + tags: [], + }, + params: getInvestigationParamsSchema, + handler: async (params) => { + const soClient = (await params.context.core).savedObjects.client; + const repository = investigationRepositoryFactory({ soClient, logger: params.logger }); + + return await getInvestigation(params.params.path, repository); + }, +}); + export function getGlobalInvestigateAppServerRouteRepository() { - return {}; + return { + ...createInvestigationRoute, + ...findInvestigationsRoute, + ...getInvestigationRoute, + }; } export type InvestigateAppServerRouteRepository = ReturnType< diff --git a/x-pack/plugins/observability_solution/investigate_app/server/saved_objects/investigation.ts b/x-pack/plugins/observability_solution/investigate_app/server/saved_objects/investigation.ts new file mode 100644 index 0000000000000..47fa9f17749c1 --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/server/saved_objects/investigation.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 { SavedObjectsType } from '@kbn/core-saved-objects-server'; +import { SavedObject } from '@kbn/core/server'; +import { StoredInvestigation } from '../models/investigation'; + +export const SO_INVESTIGATION_TYPE = 'investigation'; + +export const investigation: SavedObjectsType = { + name: SO_INVESTIGATION_TYPE, + hidden: false, + namespaceType: 'multiple-isolated', + switchToModelVersionAt: '8.10.0', + mappings: { + dynamic: false, + properties: { + id: { type: 'keyword' }, + title: { type: 'text' }, + }, + }, + management: { + displayName: 'Investigation', + importableAndExportable: false, + getTitle(savedObject: SavedObject<StoredInvestigation>) { + return `Investigation: [${savedObject.attributes.title}]`; + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/investigate_app/server/services/create_investigation.ts b/x-pack/plugins/observability_solution/investigate_app/server/services/create_investigation.ts new file mode 100644 index 0000000000000..9d28136c06f6c --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/server/services/create_investigation.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CreateInvestigationInput, CreateInvestigationResponse } from '../../common/schema/create'; +import { InvestigationRepository } from './investigation_repository'; + +export async function createInvestigation( + params: CreateInvestigationInput, + repository: InvestigationRepository +): Promise<CreateInvestigationResponse> { + const investigation = { ...params, createdAt: Date.now(), createdBy: 'elastic' }; + await repository.save(investigation); + + return investigation; +} diff --git a/x-pack/plugins/observability_solution/investigate_app/server/services/find_investigations.ts b/x-pack/plugins/observability_solution/investigate_app/server/services/find_investigations.ts new file mode 100644 index 0000000000000..2c08125aff734 --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/server/services/find_investigations.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 { + FindInvestigationsParams, + FindInvestigationsResponse, + findInvestigationsResponseSchema, +} from '../../common/schema/find'; +import { InvestigationRepository } from './investigation_repository'; + +export async function findInvestigations( + params: FindInvestigationsParams, + repository: InvestigationRepository +): Promise<FindInvestigationsResponse> { + const investigations = await repository.search(toPagination(params)); + + return findInvestigationsResponseSchema.encode(investigations); +} + +function toPagination(params: FindInvestigationsParams) { + const DEFAULT_PER_PAGE = 10; + const DEFAULT_PAGE = 1; + return { + page: params.page ? parseInt(params.page, 10) : DEFAULT_PAGE, + perPage: params.perPage ? parseInt(params.perPage, 10) : DEFAULT_PER_PAGE, + }; +} diff --git a/x-pack/plugins/observability_solution/investigate_app/server/services/get_investigation.ts b/x-pack/plugins/observability_solution/investigate_app/server/services/get_investigation.ts new file mode 100644 index 0000000000000..9bd6025f8e8a2 --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/server/services/get_investigation.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { GetInvestigationParams, GetInvestigationResponse } from '../../common/schema/get'; +import { InvestigationRepository } from './investigation_repository'; + +export async function getInvestigation( + params: GetInvestigationParams, + repository: InvestigationRepository +): Promise<GetInvestigationResponse> { + const investigation = await repository.findById(params.id); + + return investigation; +} diff --git a/x-pack/plugins/observability_solution/investigate_app/server/services/investigation_repository.ts b/x-pack/plugins/observability_solution/investigate_app/server/services/investigation_repository.ts new file mode 100644 index 0000000000000..6d9c5ddbc0d03 --- /dev/null +++ b/x-pack/plugins/observability_solution/investigate_app/server/services/investigation_repository.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 { Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { isLeft } from 'fp-ts/lib/Either'; +import { Investigation, StoredInvestigation, investigationSchema } from '../models/investigation'; +import { SO_INVESTIGATION_TYPE } from '../saved_objects/investigation'; +import { Paginated, Pagination } from '../models/pagination'; + +export interface InvestigationRepository { + save(investigation: Investigation): Promise<void>; + findById(id: string): Promise<Investigation>; + deleteById(id: string): Promise<void>; + search(pagination: Pagination): Promise<Paginated<Investigation>>; +} + +export function investigationRepositoryFactory({ + soClient, + logger, +}: { + soClient: SavedObjectsClientContract; + logger: Logger; +}): InvestigationRepository { + function toInvestigation(stored: StoredInvestigation): Investigation | undefined { + const result = investigationSchema.decode({ + ...stored, + }); + + if (isLeft(result)) { + logger.error(`Invalid stored Investigation with id [${stored.id}]`); + return undefined; + } + + return result.right; + } + + function toStoredInvestigation(investigation: Investigation): StoredInvestigation { + return investigationSchema.encode(investigation); + } + + return { + async save(investigation: Investigation): Promise<void> { + await soClient.create<StoredInvestigation>( + SO_INVESTIGATION_TYPE, + toStoredInvestigation(investigation), + { + id: investigation.id, + overwrite: true, + } + ); + }, + + async findById(id: string): Promise<Investigation> { + const response = await soClient.find<StoredInvestigation>({ + type: SO_INVESTIGATION_TYPE, + page: 1, + perPage: 1, + filter: `investigation.attributes.id:(${id})`, + }); + + if (response.total === 0) { + throw new Error(`Investigation [${id}] not found`); + } + + const investigation = toInvestigation(response.saved_objects[0].attributes); + if (investigation === undefined) { + throw new Error('Invalid stored Investigation'); + } + + return investigation; + }, + + async deleteById(id: string): Promise<void> { + const response = await soClient.find<StoredInvestigation>({ + type: SO_INVESTIGATION_TYPE, + page: 1, + perPage: 1, + filter: `investigation.attributes.id:(${id})`, + }); + + if (response.total === 0) { + throw new Error(`Investigation [${id}] not found`); + } + + await soClient.delete(SO_INVESTIGATION_TYPE, response.saved_objects[0].id); + }, + + async search(pagination: Pagination): Promise<Paginated<Investigation>> { + const response = await soClient.find<StoredInvestigation>({ + type: SO_INVESTIGATION_TYPE, + page: pagination.page, + perPage: pagination.perPage, + }); + + return { + total: response.total, + perPage: response.per_page, + page: response.page, + results: response.saved_objects + .map((savedObject) => toInvestigation(savedObject.attributes)) + .filter((investigation) => investigation !== undefined) as Investigation[], + }; + }, + }; +} diff --git a/x-pack/plugins/observability_solution/investigate_app/tsconfig.json b/x-pack/plugins/observability_solution/investigate_app/tsconfig.json index 7ad57bf2c01a7..b4b586601ebaa 100644 --- a/x-pack/plugins/observability_solution/investigate_app/tsconfig.json +++ b/x-pack/plugins/observability_solution/investigate_app/tsconfig.json @@ -47,10 +47,10 @@ "@kbn/unified-search-plugin", "@kbn/es-query", "@kbn/server-route-repository", - "@kbn/management-settings-ids", "@kbn/security-plugin", "@kbn/ui-actions-plugin", "@kbn/esql-datagrid", "@kbn/server-route-repository-utils", + "@kbn/core-saved-objects-server", ], } diff --git a/x-pack/plugins/observability_solution/logs_data_access/common/constants.ts b/x-pack/plugins/observability_solution/logs_data_access/common/constants.ts index 83acb8bcfff15..c0caaa846f56e 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/common/constants.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/common/constants.ts @@ -5,4 +5,4 @@ * 2.0. */ -export const DEFAULT_LOG_SOURCES = ['logs-*-*']; +export const DEFAULT_LOG_SOURCES = ['logs-*-*,logs-*,filebeat-*,kibana_sample_data_logs*']; diff --git a/x-pack/plugins/observability_solution/logs_data_access/common/services/log_sources_service/log_sources_service.mocks.ts b/x-pack/plugins/observability_solution/logs_data_access/common/services/log_sources_service/log_sources_service.mocks.ts new file mode 100644 index 0000000000000..3f1f8b9db5979 --- /dev/null +++ b/x-pack/plugins/observability_solution/logs_data_access/common/services/log_sources_service/log_sources_service.mocks.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { LogSource, LogSourcesService } from './types'; + +const LOG_SOURCES: LogSource[] = [{ indexPattern: 'logs-*-*' }]; +export const createLogSourcesServiceMock = ( + logSources: LogSource[] = LOG_SOURCES +): LogSourcesService => { + let sources = logSources; + return { + async getLogSources() { + return Promise.resolve(sources); + }, + async setLogSources(nextLogSources: LogSource[]) { + sources = nextLogSources; + return Promise.resolve(); + }, + }; +}; diff --git a/x-pack/plugins/observability_solution/logs_data_access/common/services/log_sources_service/types.ts b/x-pack/plugins/observability_solution/logs_data_access/common/services/log_sources_service/types.ts new file mode 100644 index 0000000000000..0d4cb51051237 --- /dev/null +++ b/x-pack/plugins/observability_solution/logs_data_access/common/services/log_sources_service/types.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface LogSource { + indexPattern: string; +} + +export interface LogSourcesService { + getLogSources: () => Promise<LogSource[]>; + setLogSources: (sources: LogSource[]) => Promise<void>; +} diff --git a/x-pack/plugins/observability_solution/logs_data_access/common/types.ts b/x-pack/plugins/observability_solution/logs_data_access/common/types.ts index d021617f294ae..0600b162e1b3d 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/common/types.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/common/types.ts @@ -5,6 +5,4 @@ * 2.0. */ -export interface LogSource { - indexPattern: string; -} +export * from './services/log_sources_service/types'; diff --git a/x-pack/plugins/observability_solution/logs_data_access/common/ui_settings.ts b/x-pack/plugins/observability_solution/logs_data_access/common/ui_settings.ts index 500011231ee38..97259784971c1 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/common/ui_settings.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/common/ui_settings.ts @@ -23,7 +23,7 @@ export const uiSettings: Record<string, UiSettingsParams> = { value: DEFAULT_LOG_SOURCES, description: i18n.translate('xpack.logsDataAccess.logSourcesDescription', { defaultMessage: - 'Sources to be used for logs data. If the data contained in these indices is not logs data, you may experience degraded functionality.', + 'Sources to be used for logs data. If the data contained in these indices is not logs data, you may experience degraded functionality. Changes to this setting can potentially impact the sources queried in Log Threshold rules.', }), type: 'array', schema: schema.arrayOf(schema.string()), diff --git a/x-pack/plugins/observability_solution/logs_data_access/public/index.ts b/x-pack/plugins/observability_solution/logs_data_access/public/index.ts index ed4a2be8a1b09..bdb45d3d0a490 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/public/index.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/public/index.ts @@ -11,6 +11,9 @@ import { LogsDataAccessPluginSetup, LogsDataAccessPluginStart, } from './plugin'; + +export type { LogsDataAccessPluginSetup, LogsDataAccessPluginStart }; + import { LogsDataAccessPluginSetupDeps, LogsDataAccessPluginStartDeps } from './types'; export const plugin: PluginInitializer< diff --git a/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts b/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts index 3fd4674ea5509..a75bbd65c26e3 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/public/services/log_sources_service/index.ts @@ -6,23 +6,24 @@ */ import { OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID } from '@kbn/management-settings-ids'; -import { LogSource } from '../../../common/types'; +import { LogSource, LogSourcesService } from '../../../common/services/log_sources_service/types'; import { RegisterServicesParams } from '../register_services'; -export function createLogSourcesService(params: RegisterServicesParams) { +export function createLogSourcesService(params: RegisterServicesParams): LogSourcesService { const { uiSettings } = params.deps; return { - getLogSources: (): LogSource[] => { + getLogSources: async () => { const logSources = uiSettings.get<string[]>(OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID); return logSources.map((logSource) => ({ indexPattern: logSource, })); }, setLogSources: async (sources: LogSource[]) => { - return await uiSettings.set( + await uiSettings.set( OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID, sources.map((source) => source.indexPattern) ); + return; }, }; } diff --git a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts index 1d80171e7d0b0..ab8ae16ba4455 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_error_rate_timeseries/get_logs_error_rate_timeseries.ts @@ -7,10 +7,10 @@ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { AggregationOptionsByType, AggregationResultOf } from '@kbn/es-types'; import { ElasticsearchClient } from '@kbn/core/server'; -import { existsQuery, kqlQuery } from '@kbn/observability-plugin/server'; import { estypes } from '@elastic/elasticsearch'; import { getBucketSizeFromTimeRangeAndBucketCount, getLogErrorRate } from '../../utils'; import { LOG_LEVEL } from '../../es_fields'; +import { existsQuery, kqlQuery } from '../../utils/es_queries'; export interface LogsErrorRateTimeseries { esClient: ElasticsearchClient; diff --git a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts index 49b2d9cb578fb..3341170832a39 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/server/services/get_logs_rate_timeseries/get_logs_rate_timeseries.ts @@ -7,10 +7,10 @@ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { AggregationOptionsByType, AggregationResultOf } from '@kbn/es-types'; import { ElasticsearchClient } from '@kbn/core/server'; -import { existsQuery, kqlQuery } from '@kbn/observability-plugin/server'; import { estypes } from '@elastic/elasticsearch'; import { getBucketSizeFromTimeRangeAndBucketCount } from '../../utils'; import { LOG_LEVEL } from '../../es_fields'; +import { existsQuery, kqlQuery } from '../../utils/es_queries'; export interface LogsRateTimeseries { esClient: ElasticsearchClient; diff --git a/x-pack/plugins/observability_solution/logs_data_access/server/services/log_sources_service/index.ts b/x-pack/plugins/observability_solution/logs_data_access/server/services/log_sources_service/index.ts index c6075d1d20834..e8907d7537932 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/server/services/log_sources_service/index.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/server/services/log_sources_service/index.ts @@ -5,31 +5,40 @@ * 2.0. */ -import { KibanaRequest } from '@kbn/core-http-server'; +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import { OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID } from '@kbn/management-settings-ids'; -import { LogSource } from '../../../common/types'; +import { LogSource, LogSourcesService } from '../../../common/services/log_sources_service/types'; import { RegisterServicesParams } from '../register_services'; -export function createGetLogSourcesService(params: RegisterServicesParams) { - return async (request: KibanaRequest) => { - const { savedObjects, uiSettings } = params.deps; - const soClient = savedObjects.getScopedClient(request); - const uiSettingsClient = uiSettings.asScopedToClient(soClient); - return { - getLogSources: async (): Promise<LogSource[]> => { - const logSources = await uiSettingsClient.get<string[]>( - OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID - ); - return logSources.map((logSource) => ({ - indexPattern: logSource, - })); - }, - setLogSources: async (sources: LogSource[]) => { - return await uiSettingsClient.set( - OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID, - sources.map((source) => source.indexPattern) - ); - }, - }; +export function createLogSourcesServiceFactory(params: RegisterServicesParams) { + return { + async getLogSourcesService( + savedObjectsClient: SavedObjectsClientContract + ): Promise<LogSourcesService> { + const { uiSettings } = params.deps; + const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient); + return { + getLogSources: async () => { + const logSources = await uiSettingsClient.get<string[]>( + OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID + ); + return logSources.map((logSource) => ({ + indexPattern: logSource, + })); + }, + setLogSources: async (sources: LogSource[]) => { + return await uiSettingsClient.set( + OBSERVABILITY_LOGS_DATA_ACCESS_LOG_SOURCES_ID, + sources.map((source) => source.indexPattern) + ); + }, + }; + }, + async getScopedLogSourcesService(request: KibanaRequest): Promise<LogSourcesService> { + const { savedObjects } = params.deps; + const soClient = savedObjects.getScopedClient(request); + return this.getLogSourcesService(soClient); + }, }; } diff --git a/x-pack/plugins/observability_solution/logs_data_access/server/services/register_services.ts b/x-pack/plugins/observability_solution/logs_data_access/server/services/register_services.ts index 4756bb17f25b1..98c0c95530672 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/server/services/register_services.ts +++ b/x-pack/plugins/observability_solution/logs_data_access/server/services/register_services.ts @@ -11,7 +11,7 @@ import { Logger } from '@kbn/logging'; import { createGetLogsRateTimeseries } from './get_logs_rate_timeseries/get_logs_rate_timeseries'; import { createGetLogErrorRateTimeseries } from './get_logs_error_rate_timeseries/get_logs_error_rate_timeseries'; import { createGetLogsRatesService } from './get_logs_rates_service'; -import { createGetLogSourcesService } from './log_sources_service'; +import { createLogSourcesServiceFactory } from './log_sources_service'; export interface RegisterServicesParams { logger: Logger; @@ -26,6 +26,6 @@ export function registerServices(params: RegisterServicesParams) { getLogsRatesService: createGetLogsRatesService(), getLogsRateTimeseries: createGetLogsRateTimeseries(), getLogsErrorRateTimeseries: createGetLogErrorRateTimeseries(), - getLogSourcesService: createGetLogSourcesService(params), + logSourcesServiceFactory: createLogSourcesServiceFactory(params), }; } diff --git a/x-pack/plugins/observability_solution/logs_data_access/server/utils/es_queries.ts b/x-pack/plugins/observability_solution/logs_data_access/server/utils/es_queries.ts new file mode 100644 index 0000000000000..282b21af495c9 --- /dev/null +++ b/x-pack/plugins/observability_solution/logs_data_access/server/utils/es_queries.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 { estypes } from '@elastic/elasticsearch'; +import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; + +export function existsQuery(field: string): QueryDslQueryContainer[] { + return [{ exists: { field } }]; +} + +export function kqlQuery(kql?: string): estypes.QueryDslQueryContainer[] { + if (!kql) { + return []; + } + + const ast = fromKueryExpression(kql); + return [toElasticsearchQuery(ast)]; +} diff --git a/x-pack/plugins/observability_solution/logs_data_access/tsconfig.json b/x-pack/plugins/observability_solution/logs_data_access/tsconfig.json index 09a57dea36e49..45c96d862d9ff 100644 --- a/x-pack/plugins/observability_solution/logs_data_access/tsconfig.json +++ b/x-pack/plugins/observability_solution/logs_data_access/tsconfig.json @@ -9,7 +9,6 @@ "@kbn/core", "@kbn/data-plugin", "@kbn/data-views-plugin", - "@kbn/observability-plugin", "@kbn/calculate-auto", "@kbn/es-types", "@kbn/core-http-server", @@ -20,6 +19,8 @@ "@kbn/core-saved-objects-server", "@kbn/core-ui-settings-server", "@kbn/core-ui-settings-browser", - "@kbn/logging" + "@kbn/logging", + "@kbn/core-saved-objects-api-server", + "@kbn/es-query" ] } diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/translations.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/common/translations.tsx index 577e068483427..826fcdab65915 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/translations.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/components/common/translations.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiCode } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; export const contentLabel = i18n.translate('xpack.logsExplorer.dataTable.header.popover.content', { @@ -21,17 +20,6 @@ export const resourceLabel = i18n.translate( } ); -export const actionsLabel = i18n.translate('xpack.logsExplorer.dataTable.header.popover.actions', { - defaultMessage: 'Actions', -}); - -export const actionsLabelLowerCase = i18n.translate( - 'xpack.logsExplorer.dataTable.header.popover.actions.lowercase', - { - defaultMessage: 'actions', - } -); - export const actionFilterForText = (text: string) => i18n.translate('xpack.logsExplorer.flyoutDetail.value.hover.filterFor', { defaultMessage: 'Filter for this {value}', @@ -109,35 +97,18 @@ export const resourceHeaderTooltipParagraph = i18n.translate( } ); -export const actionsHeaderTooltipParagraph = i18n.translate( - 'xpack.logsExplorer.dataTable.header.actions.tooltip.paragraph', +export const actionsHeaderAriaLabelDegradedAction = i18n.translate( + 'xpack.logsExplorer.dataTable.controlColumnHeader.degradedDocArialLabel', { - defaultMessage: 'Fields that provide actionable information, such as:', + defaultMessage: 'Access to degraded docs', } ); -export const actionsHeaderTooltipExpandAction = i18n.translate( - 'xpack.logsExplorer.dataTable.header.actions.tooltip.expand', - { defaultMessage: 'Expand log details' } -); - -export const actionsHeaderTooltipDegradedAction = ( - <FormattedMessage - id="xpack.logsExplorer.dataTable.controlColumn.actions.button.degradedDoc" - defaultMessage="Access to degraded doc with {ignoredProperty} field" - values={{ - ignoredProperty: ( - <EuiCode language="json" transparentBackground> - _ignored - </EuiCode> - ), - }} - /> -); - -export const actionsHeaderTooltipStacktraceAction = i18n.translate( - 'xpack.logsExplorer.dataTable.header.actions.tooltip.stacktrace', - { defaultMessage: 'Access to available stacktraces based on:' } +export const actionsHeaderAriaLabelStacktraceAction = i18n.translate( + 'xpack.logsExplorer.dataTable.controlColumnHeader.stacktraceArialLabel', + { + defaultMessage: 'Access to available stacktraces', + } ); export const degradedDocButtonLabelWhenPresent = i18n.translate( diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/actions_column_tooltip.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/actions_column_tooltip.tsx deleted file mode 100644 index c33c514bf3789..0000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/actions_column_tooltip.tsx +++ /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 React from 'react'; -import { css } from '@emotion/react'; -import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; -import { euiThemeVars } from '@kbn/ui-theme'; -import { - actionsHeaderTooltipExpandAction, - actionsHeaderTooltipDegradedAction, - actionsHeaderTooltipParagraph, - actionsHeaderTooltipStacktraceAction, - actionsLabel, - actionsLabelLowerCase, -} from '../../common/translations'; -import { TooltipButton } from './tooltip_button'; -import * as constants from '../../../../common/constants'; -import { FieldWithToken } from './field_with_token'; - -const spacingCSS = css` - margin-bottom: ${euiThemeVars.euiSizeS}; -`; - -export const ActionsColumnTooltip = () => { - return ( - <TooltipButton displayText={actionsLabelLowerCase} popoverTitle={actionsLabel}> - <div style={{ width: '230px' }}> - <EuiText size="s" css={spacingCSS}> - <p>{actionsHeaderTooltipParagraph}</p> - </EuiText> - <EuiFlexGroup - responsive={false} - alignItems="baseline" - justifyContent="flexStart" - gutterSize="s" - css={spacingCSS} - > - <EuiFlexItem grow={false}> - <EuiIcon type="expand" size="s" /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiText size="s"> - <p>{actionsHeaderTooltipExpandAction}</p> - </EuiText> - </EuiFlexItem> - </EuiFlexGroup> - <EuiFlexGroup - responsive={false} - alignItems="baseline" - justifyContent="flexStart" - gutterSize="s" - css={spacingCSS} - > - <EuiFlexItem grow={false}> - <EuiIcon type="indexClose" size="s" color="danger" /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiText size="s"> - <p>{actionsHeaderTooltipDegradedAction}</p> - </EuiText> - </EuiFlexItem> - </EuiFlexGroup> - <EuiFlexGroup - responsive={false} - alignItems="baseline" - justifyContent="flexStart" - gutterSize="s" - css={spacingCSS} - > - <EuiFlexItem grow={false}> - <EuiIcon type="apmTrace" size="s" /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiText size="s"> - <p>{actionsHeaderTooltipStacktraceAction}</p> - </EuiText> - </EuiFlexItem> - </EuiFlexGroup> - <div style={{ marginLeft: '15px' }}> - {[ - constants.ERROR_STACK_TRACE, - constants.ERROR_EXCEPTION_STACKTRACE, - constants.ERROR_LOG_STACKTRACE, - ].map((field) => ( - <FieldWithToken field={field} key={field} /> - ))} - </div> - </div> - </TooltipButton> - ); -}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx index 43edee4cf73af..89bc38482c803 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx @@ -5,19 +5,16 @@ * 2.0. */ -import React, { ComponentClass } from 'react'; -import { - OPEN_DETAILS, - SELECT_ROW, - type ControlColumnsProps, - DataTableRowControl, -} from '@kbn/unified-data-table'; -import { EuiButtonIcon, EuiDataGridCellValueElementProps, EuiToolTip } from '@elastic/eui'; -import type { DataTableRecord } from '@kbn/discover-utils/src/types'; -import { useActor } from '@xstate/react'; +import React from 'react'; import { LogDocument } from '@kbn/discover-utils/src'; -import { LogsExplorerControllerStateService } from '../state_machines/logs_explorer_controller'; +import type { + UnifiedDataTableProps, + RowControlComponent, + RowControlRowProps, +} from '@kbn/unified-data-table'; import { + actionsHeaderAriaLabelDegradedAction, + actionsHeaderAriaLabelStacktraceAction, degradedDocButtonLabelWhenNotPresent, degradedDocButtonLabelWhenPresent, stacktraceAvailableControlButton, @@ -25,122 +22,79 @@ import { } from '../components/common/translations'; import * as constants from '../../common/constants'; import { getStacktraceFields } from '../utils/get_stack_trace'; -import { ActionsColumnTooltip } from '../components/virtual_columns/column_tooltips/actions_column_tooltip'; - -const ConnectedDegradedDocs = ({ - rowIndex, - service, -}: { - rowIndex: number; - service: LogsExplorerControllerStateService; -}) => { - const [state] = useActor(service); - if (state.matches('initialized') && state.context.rows[rowIndex]) { - return <DegradedDocs row={state.context.rows[rowIndex]} rowIndex={rowIndex} />; - } - - return null; -}; -const ConnectedStacktraceDocs = ({ - rowIndex, - service, +const DegradedDocs = ({ + Control, + rowProps: { record }, }: { - rowIndex: number; - service: LogsExplorerControllerStateService; + Control: RowControlComponent; + rowProps: RowControlRowProps; }) => { - const [state] = useActor(service); - if (state.matches('initialized') && state.context.rows[rowIndex]) { - return <Stacktrace row={state.context.rows[rowIndex]} rowIndex={rowIndex} />; - } - - return null; -}; - -const DegradedDocs = ({ row, rowIndex }: { row: DataTableRecord; rowIndex: number }) => { - const isDegradedDocumentExists = constants.DEGRADED_DOCS_FIELD in row.raw; + const isDegradedDocumentExists = constants.DEGRADED_DOCS_FIELD in record.raw; return isDegradedDocumentExists ? ( - <DataTableRowControl> - <EuiToolTip content={degradedDocButtonLabelWhenPresent} delay="long"> - <EuiButtonIcon - id={`degradedDocExists_${rowIndex}`} - size="xs" - iconSize="s" - data-test-subj={'docTableDegradedDocExist'} - color={'danger'} - aria-label={degradedDocButtonLabelWhenPresent} - iconType={'indexClose'} - /> - </EuiToolTip> - </DataTableRowControl> + <Control + data-test-subj="docTableDegradedDocExist" + color="danger" + label={degradedDocButtonLabelWhenPresent} + iconType="indexClose" + onClick={undefined} + /> ) : ( - <DataTableRowControl> - <EuiToolTip content={degradedDocButtonLabelWhenNotPresent} delay="long"> - <EuiButtonIcon - id={`degradedDocExists_${rowIndex}`} - size="xs" - iconSize="s" - data-test-subj={'docTableDegradedDocDoesNotExist'} - color={'text'} - iconType={'pagesSelect'} - aria-label={degradedDocButtonLabelWhenNotPresent} - /> - </EuiToolTip> - </DataTableRowControl> + <Control + data-test-subj="docTableDegradedDocDoesNotExist" + color="text" + label={degradedDocButtonLabelWhenNotPresent} + iconType="indexClose" + onClick={undefined} + /> ); }; -const Stacktrace = ({ row, rowIndex }: { row: DataTableRecord; rowIndex: number }) => { - const stacktrace = getStacktraceFields(row as LogDocument); +const Stacktrace = ({ + Control, + rowProps: { record }, +}: { + Control: RowControlComponent; + rowProps: RowControlRowProps; +}) => { + const stacktrace = getStacktraceFields(record as LogDocument); const hasValue = Object.values(stacktrace).some((value) => value); - return ( - <DataTableRowControl> - <EuiToolTip - content={hasValue ? stacktraceAvailableControlButton : stacktraceNotAvailableControlButton} - delay="long" - > - <EuiButtonIcon - id={`stacktrace_${rowIndex}`} - size="xs" - iconSize="s" - data-test-subj={hasValue ? 'docTableStacktraceExist' : 'docTableStacktraceDoesNotExist'} - color={'text'} - iconType={'apmTrace'} - aria-label={ - hasValue ? stacktraceAvailableControlButton : stacktraceNotAvailableControlButton - } - disabled={!hasValue} - /> - </EuiToolTip> - </DataTableRowControl> + return hasValue ? ( + <Control + data-test-subj="docTableStacktraceExist" + label={stacktraceAvailableControlButton} + iconType="apmTrace" + onClick={undefined} + /> + ) : ( + <Control + disabled + data-test-subj="docTableStacktraceDoesNotExist" + label={stacktraceNotAvailableControlButton} + iconType="apmTrace" + onClick={undefined} + /> ); }; -export const createCustomControlColumnsConfiguration = - (service: LogsExplorerControllerStateService) => - ({ controlColumns }: ControlColumnsProps) => { - const checkBoxColumn = controlColumns[SELECT_ROW]; - const openDetails = controlColumns[OPEN_DETAILS]; - const ExpandButton = - openDetails.rowCellRender as ComponentClass<EuiDataGridCellValueElementProps>; - const actionsColumn = { - id: 'actionsColumn', - width: constants.ACTIONS_COLUMN_WIDTH, - headerCellRender: ActionsColumnTooltip, - rowCellRender: ({ rowIndex, setCellProps, ...rest }: EuiDataGridCellValueElementProps) => { - return ( - <span> - <ExpandButton rowIndex={rowIndex} setCellProps={setCellProps} {...rest} /> - <ConnectedDegradedDocs rowIndex={rowIndex} service={service} /> - <ConnectedStacktraceDocs rowIndex={rowIndex} service={service} /> - </span> - ); +export const getRowAdditionalControlColumns = + (): UnifiedDataTableProps['rowAdditionalLeadingControls'] => { + return [ + { + id: 'connectedDegradedDocs', + headerAriaLabel: actionsHeaderAriaLabelDegradedAction, + renderControl: (Control, rowProps) => { + return <DegradedDocs Control={Control} rowProps={rowProps} />; + }, }, - }; - - return { - leadingControlColumns: [checkBoxColumn, actionsColumn], - }; + { + id: 'connectedStacktraceDocs', + headerAriaLabel: actionsHeaderAriaLabelStacktraceAction, + renderControl: (Control, rowProps) => { + return <Stacktrace Control={Control} rowProps={rowProps} />; + }, + }, + ]; }; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx index 7b193f4aafff8..557cbe4dd2728 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx @@ -82,8 +82,8 @@ export const createLogsExplorerProfileCustomizations = customizations.set({ id: 'data_table', logsEnabled: true, - customControlColumnsConfiguration: await import('./custom_control_column').then((module) => - module.createCustomControlColumnsConfiguration(service) + rowAdditionalLeadingControls: await import('./custom_control_column').then((module) => + module.getRowAdditionalControlColumns() ), }); diff --git a/x-pack/plugins/observability_solution/logs_shared/common/index.ts b/x-pack/plugins/observability_solution/logs_shared/common/index.ts index f6b1e9ea27e43..104b68ce2fddf 100644 --- a/x-pack/plugins/observability_solution/logs_shared/common/index.ts +++ b/x-pack/plugins/observability_solution/logs_shared/common/index.ts @@ -17,12 +17,14 @@ export { logViewReferenceRT, persistedLogViewReferenceRT, defaultLogViewAttributes, + logSourcesKibanaAdvancedSettingRT, } from './log_views'; // LogView types export type { LogDataViewReference, LogIndexNameReference, + LogSourcesKibanaAdvancedSettingReference, LogIndexReference, LogView, LogViewAttributes, diff --git a/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.mock.ts b/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.mock.ts index 8c09f16e3b53e..568a25ecef1d7 100644 --- a/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.mock.ts +++ b/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.mock.ts @@ -11,6 +11,7 @@ import { defaultLogViewsStaticConfig } from './defaults'; import { ResolvedLogView, resolveLogView } from './resolved_log_view'; import { LogViewAttributes } from './types'; import { DataViewSpec } from '@kbn/data-views-plugin/common'; +import { createLogSourcesServiceMock } from '@kbn/logs-data-access-plugin/common/services/log_sources_service/log_sources_service.mocks'; export const createResolvedLogViewMock = ( resolvedLogViewOverrides: Partial<ResolvedLogView> = {} @@ -63,5 +64,6 @@ export const createResolvedLogViewMockFromAttributes = (logViewAttributes: LogVi spec, }), } as unknown as DataViewsContract, + createLogSourcesServiceMock(), defaultLogViewsStaticConfig ); diff --git a/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.ts b/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.ts index b9419fbd51f22..1521aa67e3d92 100644 --- a/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.ts +++ b/x-pack/plugins/observability_solution/logs_shared/common/log_views/resolved_log_view.ts @@ -7,6 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { DataView, DataViewsContract, FieldSpec } from '@kbn/data-views-plugin/common'; +import { LogSourcesService } from '@kbn/logs-data-access-plugin/common/services/log_sources_service/types'; import { TIEBREAKER_FIELD, TIMESTAMP_FIELD } from '../constants'; import { defaultLogViewsStaticConfig } from './defaults'; import { ResolveLogViewError } from './errors'; @@ -31,12 +32,20 @@ export const resolveLogView = ( logViewId: string, logViewAttributes: LogViewAttributes, dataViewsService: DataViewsContract, + logSourcesService: LogSourcesService, config: LogViewsStaticConfig ): Promise<ResolvedLogView> => { if (logViewAttributes.logIndices.type === 'index_name') { return resolveLegacyReference(logViewId, logViewAttributes, dataViewsService, config); - } else { + } else if (logViewAttributes.logIndices.type === 'data_view') { return resolveDataViewReference(logViewAttributes, dataViewsService); + } else { + return resolveKibanaAdvancedSettingReference( + logViewId, + logViewAttributes, + dataViewsService, + logSourcesService + ); } }; @@ -110,6 +119,52 @@ const resolveDataViewReference = async ( }; }; +const resolveKibanaAdvancedSettingReference = async ( + logViewId: string, + logViewAttributes: LogViewAttributes, + dataViewsService: DataViewsContract, + logSourcesService: LogSourcesService +): Promise<ResolvedLogView> => { + if (logViewAttributes.logIndices.type !== 'kibana_advanced_setting') { + throw new Error( + 'This function can only resolve references to the Log Sources Kibana advanced setting' + ); + } + + const indices = (await logSourcesService.getLogSources()) + .map((logSource) => logSource.indexPattern) + .join(','); + + const dataViewReference = await dataViewsService + .create( + { + id: `log-view-${logViewId}`, + name: logViewAttributes.name, + title: indices, + timeFieldName: TIMESTAMP_FIELD, + allowNoIndex: true, + }, + false, + false + ) + .catch((error) => { + throw new ResolveLogViewError(`Failed to create Data View reference: ${error}`, error); + }); + + return { + indices, + timestampField: TIMESTAMP_FIELD, + tiebreakerField: TIEBREAKER_FIELD, + messageField: ['message'], + fields: dataViewReference.fields, + runtimeMappings: {}, + columns: logViewAttributes.logColumns, + name: logViewAttributes.name, + description: logViewAttributes.description, + dataViewReference, + }; +}; + // this might take other sources of runtime fields into account in the future const resolveRuntimeMappings = (dataView: DataView): estypes.MappingRuntimeFields => { return dataView.getRuntimeMappings(); diff --git a/x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts b/x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts index f94601f9e0f84..6cc8d60191a34 100644 --- a/x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts +++ b/x-pack/plugins/observability_solution/logs_shared/common/log_views/types.ts @@ -38,7 +38,20 @@ export const logIndexNameReferenceRT = rt.type({ }); export type LogIndexNameReference = rt.TypeOf<typeof logIndexNameReferenceRT>; -export const logIndexReferenceRT = rt.union([logDataViewReferenceRT, logIndexNameReferenceRT]); +// Kibana advanced setting +export const logSourcesKibanaAdvancedSettingRT = rt.type({ + type: rt.literal('kibana_advanced_setting'), +}); + +export type LogSourcesKibanaAdvancedSettingReference = rt.TypeOf< + typeof logSourcesKibanaAdvancedSettingRT +>; + +export const logIndexReferenceRT = rt.union([ + logDataViewReferenceRT, + logIndexNameReferenceRT, + logSourcesKibanaAdvancedSettingRT, +]); export type LogIndexReference = rt.TypeOf<typeof logIndexReferenceRT>; const logViewCommonColumnConfigurationRT = rt.strict({ diff --git a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc index 7e79614b56e5a..ea93fd326dac7 100644 --- a/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc +++ b/x-pack/plugins/observability_solution/logs_shared/kibana.jsonc @@ -14,7 +14,8 @@ "discoverShared", "usageCollection", "observabilityShared", - "share" + "share", + "logsDataAccess" ], "optionalPlugins": [ "observabilityAIAssistant", diff --git a/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx b/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx index 5ba062912d8f3..f0c9c411249a9 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx +++ b/x-pack/plugins/observability_solution/logs_shared/public/components/log_stream/log_stream.tsx @@ -15,6 +15,7 @@ import { JsonValue } from '@kbn/utility-types'; import { noop } from 'lodash'; import React, { useCallback, useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; +import type { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; import { LogEntryCursor } from '../../../common/log_entry'; import { defaultLogViewsStaticConfig, LogViewReference } from '../../../common/log_views'; import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream'; @@ -28,6 +29,7 @@ import { LogStreamErrorBoundary } from './log_stream_error_boundary'; interface LogStreamPluginDeps { data: DataPublicPluginStart; + logsDataAccess: LogsDataAccessPluginStart; http: HttpStart; share: SharePluginStart; } @@ -113,9 +115,9 @@ export const LogStreamContent = ({ ); const { - services: { http, data, share }, + services: { http, data, share, logsDataAccess }, } = useKibana<LogStreamPluginDeps>(); - if (http == null || data == null || share == null) { + if (http == null || data == null || share == null || logsDataAccess == null) { throw new Error( `<LogStream /> cannot access kibana core services. @@ -130,8 +132,15 @@ Read more at https://github.com/elastic/kibana/blob/main/src/plugins/kibana_reac const kibanaQuerySettings = useKibanaQuerySettings(); const logViews = useMemo( - () => new LogViewsClient(data.dataViews, http, data.search.search, defaultLogViewsStaticConfig), - [data.dataViews, data.search.search, http] + () => + new LogViewsClient( + data.dataViews, + logsDataAccess.services.logSourcesService, + http, + data.search.search, + defaultLogViewsStaticConfig + ), + [data.dataViews, data.search.search, http, logsDataAccess.services.logSourcesService] ); const { diff --git a/x-pack/plugins/observability_solution/logs_shared/public/plugin.ts b/x-pack/plugins/observability_solution/logs_shared/public/plugin.ts index 4655a7a62f087..d6f4ac81fe266 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/plugin.ts +++ b/x-pack/plugins/observability_solution/logs_shared/public/plugin.ts @@ -52,11 +52,12 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass { public start(core: CoreStart, plugins: LogsSharedClientStartDeps) { const { http } = core; - const { data, dataViews, discoverShared, observabilityAIAssistant } = plugins; + const { data, dataViews, discoverShared, observabilityAIAssistant, logsDataAccess } = plugins; const logViews = this.logViews.start({ http, dataViews, + logSourcesService: logsDataAccess.services.logSourcesService, search: data.search, }); diff --git a/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_client.ts b/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_client.ts index a53ac542c5f03..b1a71cea73cb1 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_client.ts +++ b/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_client.ts @@ -10,6 +10,7 @@ import { HttpStart } from '@kbn/core/public'; import type { ISearchGeneric } from '@kbn/search-types'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; import { lastValueFrom } from 'rxjs'; +import { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types'; import { getLogViewResponsePayloadRT, putLogViewRequestPayloadRT } from '../../../common/http_api'; import { getLogViewUrl } from '../../../common/http_api/log_views'; import { @@ -31,6 +32,7 @@ import { ILogViewsClient } from './types'; export class LogViewsClient implements ILogViewsClient { constructor( private readonly dataViews: DataViewsContract, + private readonly logSourcesService: LogSourcesService, private readonly http: HttpStart, private readonly search: ISearchGeneric, private readonly config: LogViewsStaticConfig @@ -152,7 +154,13 @@ export class LogViewsClient implements ILogViewsClient { logViewId: string, logViewAttributes: LogViewAttributes ): Promise<ResolvedLogView> { - return await resolveLogView(logViewId, logViewAttributes, this.dataViews, this.config); + return await resolveLogView( + logViewId, + logViewAttributes, + this.dataViews, + this.logSourcesService, + this.config + ); } } diff --git a/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_service.ts b/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_service.ts index 712196c95205c..66ddfde911176 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_service.ts +++ b/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/log_views_service.ts @@ -20,8 +20,19 @@ export class LogViewsService { }; } - public start({ dataViews, http, search }: LogViewsServiceStartDeps): LogViewsServiceStart { - const client = new LogViewsClient(dataViews, http, search.search, this.logViewsStaticConfig); + public start({ + dataViews, + http, + search, + logSourcesService, + }: LogViewsServiceStartDeps): LogViewsServiceStart { + const client = new LogViewsClient( + dataViews, + logSourcesService, + http, + search.search, + this.logViewsStaticConfig + ); return { client, diff --git a/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/types.ts b/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/types.ts index 58a504be789bc..a6d516f00669d 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/types.ts +++ b/x-pack/plugins/observability_solution/logs_shared/public/services/log_views/types.ts @@ -8,6 +8,7 @@ import { HttpStart } from '@kbn/core/public'; import { ISearchStart } from '@kbn/data-plugin/public'; import { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types'; import { LogView, LogViewAttributes, @@ -29,6 +30,7 @@ export interface LogViewsServiceStartDeps { dataViews: DataViewsContract; http: HttpStart; search: ISearchStart; + logSourcesService: LogSourcesService; } export interface ILogViewsClient { diff --git a/x-pack/plugins/observability_solution/logs_shared/public/types.ts b/x-pack/plugins/observability_solution/logs_shared/public/types.ts index 9f0d344294880..58b180ee8b6ef 100644 --- a/x-pack/plugins/observability_solution/logs_shared/public/types.ts +++ b/x-pack/plugins/observability_solution/logs_shared/public/types.ts @@ -9,6 +9,7 @@ import type { CoreSetup, CoreStart, Plugin as PluginClass } from '@kbn/core/publ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public'; +import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/public'; import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public'; import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; @@ -37,6 +38,7 @@ export interface LogsSharedClientStartDeps { data: DataPublicPluginStart; dataViews: DataViewsPublicPluginStart; discoverShared: DiscoverSharedPublicStart; + logsDataAccess: LogsDataAccessPluginStart; observabilityAIAssistant?: ObservabilityAIAssistantPublicStart; share: SharePluginStart; uiActions: UiActionsStart; diff --git a/x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts index 2601167f2d988..f3cbfb57b09c4 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -172,10 +172,13 @@ export class LogsSharedLogEntriesDomain implements ILogsSharedLogEntriesDomain { params: LogEntriesParams, columnOverrides?: LogViewColumnConfiguration[] ): Promise<{ entries: LogEntry[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }> { - const [, , { logViews }] = await this.libs.getStartServices(); + const [, { logsDataAccess }, { logViews }] = await this.libs.getStartServices(); const { savedObjects, elasticsearch } = await requestContext.core; + const logSourcesService = logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService( + savedObjects.client + ); const resolvedLogView = await logViews - .getClient(savedObjects.client, elasticsearch.client.asCurrentUser) + .getClient(savedObjects.client, elasticsearch.client.asCurrentUser, logSourcesService) .getResolvedLogView(logView); const columnDefinitions = columnOverrides ?? resolvedLogView.columns; @@ -232,11 +235,15 @@ export class LogsSharedLogEntriesDomain implements ILogsSharedLogEntriesDomain { bucketSize: number, filterQuery?: LogEntryQuery ): Promise<LogEntriesSummaryBucket[]> { - const [, , { logViews }] = await this.libs.getStartServices(); + const [, { logsDataAccess }, { logViews }] = await this.libs.getStartServices(); const { savedObjects, elasticsearch } = await requestContext.core; + const logSourcesService = logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService( + savedObjects.client + ); const resolvedLogView = await logViews - .getClient(savedObjects.client, elasticsearch.client.asCurrentUser) + .getClient(savedObjects.client, elasticsearch.client.asCurrentUser, logSourcesService) .getResolvedLogView(logView); + const dateRangeBuckets = await this.adapter.getContainedLogSummaryBuckets( requestContext, resolvedLogView, @@ -257,11 +264,16 @@ export class LogsSharedLogEntriesDomain implements ILogsSharedLogEntriesDomain { highlightQueries: string[], filterQuery?: LogEntryQuery ): Promise<LogEntriesSummaryHighlightsBucket[][]> { - const [, , { logViews }] = await this.libs.getStartServices(); + const [, { logsDataAccess }, { logViews }] = await this.libs.getStartServices(); const { savedObjects, elasticsearch } = await requestContext.core; + const logSourcesService = logsDataAccess.services.logSourcesServiceFactory.getLogSourcesService( + savedObjects.client + ); + const resolvedLogView = await logViews - .getClient(savedObjects.client, elasticsearch.client.asCurrentUser) + .getClient(savedObjects.client, elasticsearch.client.asCurrentUser, logSourcesService) .getResolvedLogView(logView); + const messageFormattingRules = compileFormattingRules( getBuiltinRules(resolvedLogView.messageField) ); diff --git a/x-pack/plugins/observability_solution/logs_shared/server/plugin.ts b/x-pack/plugins/observability_solution/logs_shared/server/plugin.ts index 7e3fad84e4c30..6bc9560764a7b 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/plugin.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/plugin.ts @@ -97,6 +97,7 @@ export class LogsSharedPlugin const logViews = this.logViews.start({ savedObjects: core.savedObjects, dataViews: plugins.dataViews, + logsDataAccess: plugins.logsDataAccess, elasticsearch: core.elasticsearch, }); diff --git a/x-pack/plugins/observability_solution/logs_shared/server/saved_objects/log_view/types.ts b/x-pack/plugins/observability_solution/logs_shared/server/saved_objects/log_view/types.ts index fb8bf49781a2d..341e9bbed608b 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/saved_objects/log_view/types.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/saved_objects/log_view/types.ts @@ -19,9 +19,14 @@ export const logIndexNameSavedObjectReferenceRT = rt.type({ indexName: rt.string, }); +export const logSourcesKibanaAdvancedSettingSavedObjectRT = rt.type({ + type: rt.literal('kibana_advanced_setting'), +}); + export const logIndexSavedObjectReferenceRT = rt.union([ logDataViewSavedObjectReferenceRT, logIndexNameSavedObjectReferenceRT, + logSourcesKibanaAdvancedSettingSavedObjectRT, ]); const logViewSavedObjectCommonColumnConfigurationRT = rt.strict({ diff --git a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts index a1175432a4ac7..f6df48b22ba7e 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.test.ts @@ -22,6 +22,7 @@ import { logViewSavedObjectName, } from '../../saved_objects/log_view'; import { LogViewsClient } from './log_views_client'; +import { createLogSourcesServiceMock } from '@kbn/logs-data-access-plugin/common/services/log_sources_service/log_sources_service.mocks'; describe('LogViewsClient class', () => { it('getLogView resolves the default id to a real saved object id if it exists', async () => { @@ -341,6 +342,7 @@ describe('LogViewsClient class', () => { const createLogViewsClient = () => { const logger = loggerMock.create(); const dataViews = dataViewsServiceMock; + const logSourcesService = createLogSourcesServiceMock(); const savedObjectsClient = savedObjectsClientMock.create(); const logViewFallbackHandler = jest.fn(); const internalLogViews = new Map<string, LogView>(); @@ -351,6 +353,7 @@ const createLogViewsClient = () => { const logViewsClient = new LogViewsClient( logger, Promise.resolve(dataViews), + Promise.resolve(logSourcesService), savedObjectsClient, logViewFallbackHandler, internalLogViews, diff --git a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.ts b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.ts index 0b9bfe9febf4a..cea43f02b358d 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_client.ts @@ -13,6 +13,7 @@ import { SavedObjectsUtils, SavedObjectsErrorHelpers, } from '@kbn/core/server'; +import { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types'; import { defaultLogViewAttributes, defaultLogViewId, @@ -44,6 +45,7 @@ export class LogViewsClient implements ILogViewsClient { constructor( private readonly logger: Logger, private readonly dataViews: DataViewsService, + private readonly logSourcesService: Promise<LogSourcesService>, private readonly savedObjectsClient: SavedObjectsClientContract, private readonly logViewFallbackHandler: LogViewFallbackHandler, private readonly internalLogViews: Map<string, LogView>, @@ -117,7 +119,13 @@ export class LogViewsClient implements ILogViewsClient { logViewId: string, logViewAttributes: LogViewAttributes ): Promise<ResolvedLogView> { - return await resolveLogView(logViewId, logViewAttributes, await this.dataViews, this.config); + return await resolveLogView( + logViewId, + logViewAttributes, + await this.dataViews, + await this.logSourcesService, + this.config + ); } private async getSavedLogView(logViewId: string): Promise<LogView> { diff --git a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.mock.ts b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.mock.ts index 295b1fd77452f..11fbe43a6b9cb 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.mock.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.mock.ts @@ -15,8 +15,9 @@ export const createLogViewsServiceSetupMock = (): jest.Mocked<LogViewsServiceSet }); export const createLogViewsServiceStartMock = (): jest.Mocked<LogViewsServiceStart> => ({ - getClient: jest.fn((_savedObjectsClient: any, _elasticsearchClient: any) => - createLogViewsClientMock() + getClient: jest.fn( + (_savedObjectsClient: any, _elasticsearchClient: any, _logSourcesService: any) => + createLogViewsClientMock() ), getScopedClient: jest.fn((_request: any) => createLogViewsClientMock()), }); diff --git a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.ts b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.ts index 5479c16dff411..2f429dc7612ff 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/log_views_service.ts @@ -11,6 +11,7 @@ import { Logger, SavedObjectsClientContract, } from '@kbn/core/server'; +import { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types'; import { defaultLogViewAttributes, defaultLogViewsStaticConfig, @@ -56,6 +57,7 @@ export class LogViewsService { public start({ dataViews, + logsDataAccess, elasticsearch, savedObjects, }: LogViewsServiceStartDeps): LogViewsServiceStart { @@ -65,11 +67,13 @@ export class LogViewsService { getClient( savedObjectsClient: SavedObjectsClientContract, elasticsearchClient: ElasticsearchClient, + logSourcesService: Promise<LogSourcesService>, request?: KibanaRequest ) { return new LogViewsClient( logger, dataViews.dataViewsServiceFactory(savedObjectsClient, elasticsearchClient, request), + logSourcesService, savedObjectsClient, logViewFallbackHandler, internalLogViews, @@ -79,8 +83,9 @@ export class LogViewsService { getScopedClient(request: KibanaRequest) { const savedObjectsClient = savedObjects.getScopedClient(request); const elasticsearchClient = elasticsearch.client.asScoped(request).asCurrentUser; - - return this.getClient(savedObjectsClient, elasticsearchClient, request); + const logSourcesService = + logsDataAccess.services.logSourcesServiceFactory.getScopedLogSourcesService(request); + return this.getClient(savedObjectsClient, elasticsearchClient, logSourcesService, request); }, }; } diff --git a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/types.ts b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/types.ts index b0aa2784e4cd2..becf32881acf8 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/types.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/services/log_views/types.ts @@ -13,6 +13,8 @@ import { SavedObjectsServiceStart, } from '@kbn/core/server'; import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server'; +import { LogSourcesService } from '@kbn/logs-data-access-plugin/common/types'; +import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/server'; import { LogView, LogViewAttributes, @@ -23,6 +25,7 @@ import { export interface LogViewsServiceStartDeps { dataViews: DataViewsServerPluginStart; + logsDataAccess: LogsDataAccessPluginStart; elasticsearch: ElasticsearchServiceStart; savedObjects: SavedObjectsServiceStart; } @@ -45,6 +48,7 @@ export interface LogViewsServiceStart { getClient( savedObjectsClient: SavedObjectsClientContract, elasticsearchClient: ElasticsearchClient, + logSourcesService: Promise<LogSourcesService>, request?: KibanaRequest ): ILogViewsClient; getScopedClient(request: KibanaRequest): ILogViewsClient; diff --git a/x-pack/plugins/observability_solution/logs_shared/server/types.ts b/x-pack/plugins/observability_solution/logs_shared/server/types.ts index 2e922eceeb183..73365ece21a14 100644 --- a/x-pack/plugins/observability_solution/logs_shared/server/types.ts +++ b/x-pack/plugins/observability_solution/logs_shared/server/types.ts @@ -11,6 +11,7 @@ import { PluginStart as DataPluginStart, } from '@kbn/data-plugin/server'; import { PluginStart as DataViewsPluginStart } from '@kbn/data-views-plugin/server'; +import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/server'; import { LogsSharedDomainLibs } from './lib/logs_shared_types'; import { LogViewsServiceSetup, LogViewsServiceStart } from './services/log_views/types'; @@ -36,6 +37,7 @@ export interface LogsSharedServerPluginSetupDeps { export interface LogsSharedServerPluginStartDeps { data: DataPluginStart; dataViews: DataViewsPluginStart; + logsDataAccess: LogsDataAccessPluginStart; } export interface UsageCollector { diff --git a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json index d826cd78618c5..f1bb2527f9311 100644 --- a/x-pack/plugins/observability_solution/logs_shared/tsconfig.json +++ b/x-pack/plugins/observability_solution/logs_shared/tsconfig.json @@ -41,5 +41,6 @@ "@kbn/react-kibana-context-theme", "@kbn/test-jest-helpers", "@kbn/router-utils", + "@kbn/logs-data-access-plugin", ] } diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts index 42e8a0b81e6c8..d45fbfb5477f1 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/index.ts @@ -42,6 +42,7 @@ export const host: InventoryModel<typeof metrics> = { metrics, requiredMetrics: [ 'hostSystemOverview', + 'hostCpuUsageTotal', 'hostCpuUsage', 'hostLoad', 'hostMemoryUsage', @@ -54,5 +55,5 @@ export const host: InventoryModel<typeof metrics> = { ...awsRequiredMetrics, ...nginxRequireMetrics, ], - tooltipMetrics: ['cpu', 'memory', 'txV2', 'rxV2'], + tooltipMetrics: ['cpuTotal', 'cpu', 'memory', 'txV2', 'rxV2'], }; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts index fa75dc071666c..3ee08c028f8fb 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts @@ -80,7 +80,7 @@ export const cpuUsageUser: LensBaseLayer = { export const cpuUsage: LensBaseLayer = { label: CPU_USAGE_LABEL, - value: '(average(system.cpu.user.pct) + average(system.cpu.system.pct)) / max(system.cpu.cores)', + value: 'average(system.cpu.total.norm.pct)', format: 'percent', decimals: 0, }; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts index 6d2bc3381fdf1..5983b2a56f817 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/index.ts @@ -22,6 +22,6 @@ export const metrics: InventoryMetricsWithCharts<HostFormulas, HostCharts> = { snapshot, getFormulas: async () => await import('./formulas').then(({ formulas }) => formulas), getCharts: async () => await import('./charts').then(({ charts }) => charts), - defaultSnapshot: 'cpu', + defaultSnapshot: 'cpuTotal', defaultTimeRangeInSeconds: 3600, // 1 hour }; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/cpu_total.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/cpu_total.ts new file mode 100644 index 0000000000000..f7b4661b65d99 --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/cpu_total.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 { MetricsUIAggregation } from '../../../types'; + +export const cpuTotal: MetricsUIAggregation = { + cpuTotal: { + avg: { + field: 'system.cpu.total.norm.pct', + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts index e6f1cf6ac1912..4bf48e287fab7 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/snapshot/index.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { cpuTotal } from './cpu_total'; import { cpu } from './cpu'; import { diskLatency } from './disk_latency'; import { diskSpaceUsage } from './disk_space_usage'; @@ -21,6 +22,7 @@ import { txV2 } from './tx_v2'; import { rxV2 } from './rx_v2'; export const snapshot = { + cpuTotal, cpu, diskLatency, diskSpaceUsage, diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts index c9accaca490f6..2ced976e564a8 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/types.ts @@ -43,6 +43,7 @@ export type InventoryItemType = rt.TypeOf<typeof ItemTypeRT>; export const InventoryMetricRT = rt.keyof({ hostSystemOverview: null, + hostCpuUsageTotal: null, hostCpuUsage: null, hostFilesystem: null, hostK8sOverview: null, @@ -348,6 +349,7 @@ export type MetricsUIAggregation = rt.TypeOf<typeof MetricsUIAggregationRT>; export const SnapshotMetricTypeKeys = { count: null, + cpuTotal: null, cpu: null, diskLatency: null, diskSpaceUsage: null, diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message_connectors.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message_connectors.tsx index 77e8289deb6c5..a264a7cd30489 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message_connectors.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/welcome_message_connectors.tsx @@ -47,7 +47,7 @@ export function WelcomeMessageConnectors({ (connectors.error.body as { statusCode: number }).statusCode === 403; return ( - <div className={fadeInClassName}> + <div className={fadeInClassName} data-test-subj="observabilityAiAssistantConnectorsError"> <EuiFlexGroup direction="row" alignItems="center" justifyContent="center" gutterSize="xs"> <EuiFlexItem grow={false}> <EuiIcon type="alert" color="danger" /> diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/functions/visualize_esql.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/functions/visualize_esql.tsx index e81e30b0beec7..404ff9e32a4db 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/functions/visualize_esql.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/functions/visualize_esql.tsx @@ -353,6 +353,7 @@ export function VisualizeESQL({ query={{ esql: query }} flyoutType="overlay" isTableView + initialRowHeight={0} /> ) : ( <lens.EmbeddableComponent @@ -375,6 +376,7 @@ export function VisualizeESQL({ dataView={dataViewAsync.value} query={{ esql: query }} flyoutType="overlay" + initialRowHeight={0} /> </EuiFlexItem> )} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view.tsx index 3ac7df9f0fcd8..da34c98b86fbc 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/routes/conversations/conversation_view.tsx @@ -135,7 +135,12 @@ export function ConversationView() { `; return ( - <EuiFlexGroup direction="row" className={containerClassName} gutterSize="none"> + <EuiFlexGroup + direction="row" + className={containerClassName} + gutterSize="none" + data-test-subj="observabilityAiAssistantConversationsPage" + > <EuiFlexItem grow={false} className={conversationListContainerName}> <ConversationList selectedConversationId={conversationId} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx index caa8a47461c4c..c329e6de8e673 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_page.tsx @@ -107,7 +107,7 @@ export function SettingsPage() { }; return ( - <> + <div data-test-subj="aiAssistantSettingsPage"> <EuiTitle size="l"> <h2> {i18n.translate( @@ -141,6 +141,6 @@ export function SettingsPage() { {selectedTabContent} <EuiSpacer size="l" /> - </> + </div> ); } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.test.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.test.tsx index 39045fdf998ab..9b7022efc324c 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.test.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/settings_tab.test.tsx @@ -83,7 +83,7 @@ describe('SettingsTab', () => { } ); - fireEvent.click(getByTestId('apmBottomBarActionsButton')); + fireEvent.click(getByTestId('observabilityAiAssistantManagementBottomBarActionsButton')); await waitFor(() => expect(windowLocationReloadMock).toHaveBeenCalledTimes(1)); }); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx index 4e7b60c780f81..5b1f1c36fd3dd 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx @@ -27,11 +27,17 @@ const settingsKeys = [ ]; export function UISettings() { - const { docLinks, settings, notifications } = useKibana().services; + const { + docLinks, + settings, + notifications, + application: { capabilities }, + } = useKibana().services; const { fields, handleFieldChange, unsavedChanges, saveAll, isSaving, cleanUnsavedChanges } = useEditableSettings(settingsKeys); + const canEditAdvancedSettings = capabilities.advancedSettings?.save; async function handleSave() { try { await saveAll(); @@ -71,7 +77,7 @@ export function UISettings() { > <FieldRow field={field} - isSavingEnabled={true} + isSavingEnabled={!!canEditAdvancedSettings} onFieldChange={handleFieldChange} unsavedChange={unsavedChanges[settingKey]} /> @@ -84,11 +90,11 @@ export function UISettings() { onDiscardChanges={cleanUnsavedChanges} onSave={handleSave} saveLabel={i18n.translate( - 'xpack.observabilityAiAssistantManagement.apmSettings.saveButton', + 'xpack.observabilityAiAssistantManagement.settings.saveButton', { defaultMessage: 'Save changes' } )} unsavedChangesCount={Object.keys(unsavedChanges).length} - appTestSubj="apm" + appTestSubj="observabilityAiAssistantManagement" areChangesInvalid={hasInvalidChanges} /> )} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts index d035a7fa02716..b4b2541416e2f 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/flow/route.ts @@ -383,7 +383,8 @@ async function ensureInstalledIntegrations( const { pkgName, installSource } = integration; if (installSource === 'registry') { - const pkg = await packageClient.ensureInstalledPackage({ pkgName }); + const installation = await packageClient.ensureInstalledPackage({ pkgName }); + const pkg = installation.package; const inputs = await packageClient.getAgentPolicyInputs(pkg.name, pkg.version); const { packageInfo } = await packageClient.getPackage(pkg.name, pkg.version); diff --git a/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_editable_settings.tsx b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_editable_settings.tsx index 235e8e2ab50f1..7f05722019d7e 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_editable_settings.tsx +++ b/x-pack/plugins/observability_solution/observability_shared/public/hooks/use_editable_settings.tsx @@ -80,6 +80,10 @@ export function useEditableSettings(settingsKeys: string[]) { async function saveAll() { if (settings && !isEmpty(unsavedChanges)) { + let updateErrorOccurred = false; + const subscription = settings.client.getUpdateErrors$().subscribe((error) => { + updateErrorOccurred = true; + }); try { setIsSaving(true); const arr = Object.entries(unsavedChanges).map(([key, value]) => @@ -88,8 +92,16 @@ export function useEditableSettings(settingsKeys: string[]) { await Promise.all(arr); setForceReloadSettings((state) => ++state); cleanUnsavedChanges(); + if (updateErrorOccurred) { + throw new Error('One or more settings updates failed'); + } + } catch (e) { + throw e; } finally { setIsSaving(false); + if (subscription) { + subscription.unsubscribe(); + } } } } diff --git a/x-pack/plugins/observability_solution/profiling/e2e/cypress/e2e/profiling_views/storage_explorer.cy.ts b/x-pack/plugins/observability_solution/profiling/e2e/cypress/e2e/profiling_views/storage_explorer.cy.ts index 5f9f69180e449..db752969f14dd 100644 --- a/x-pack/plugins/observability_solution/profiling/e2e/cypress/e2e/profiling_views/storage_explorer.cy.ts +++ b/x-pack/plugins/observability_solution/profiling/e2e/cypress/e2e/profiling_views/storage_explorer.cy.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + describe('Storage explorer page', () => { const rangeFrom = '2023-04-18T00:00:00.000Z'; const rangeTo = '2023-04-18T00:05:00.000Z'; @@ -59,6 +60,40 @@ describe('Storage explorer page', () => { }); }); + describe('summary stats', () => { + it('will still load with kuery', () => { + cy.intercept('GET', '/internal/profiling/storage_explorer/summary?*', { + fixture: 'storage_explorer_summary.json', + }).as('summaryStats'); + cy.visitKibana('/app/profiling/storage-explorer', { + rangeFrom, + rangeTo, + kuery: 'host.id : "1234"', + }); + cy.wait('@summaryStats').then(({ request, response }) => { + const { + dailyDataGenerationBytes, + diskSpaceUsedPct, + totalNumberOfDistinctProbabilisticValues, + totalNumberOfHosts, + totalProfilingSizeBytes, + totalSymbolsSizeBytes, + } = response?.body; + + const { kuery } = request.query; + + expect(parseFloat(dailyDataGenerationBytes)).to.be.gt(0); + expect(parseFloat(diskSpaceUsedPct)).to.be.gt(0); + expect(parseFloat(totalNumberOfDistinctProbabilisticValues)).to.be.gt(0); + expect(parseFloat(totalNumberOfHosts)).to.be.gt(0); + expect(parseFloat(totalProfilingSizeBytes)).to.be.gt(0); + expect(parseFloat(totalSymbolsSizeBytes)).to.be.gt(0); + /* eslint-disable @typescript-eslint/no-unused-expressions */ + expect(kuery).to.be.empty; + }); + }); + }); + describe('Data breakdown', () => { it('displays correct values per index', () => { cy.intercept('GET', '/internal/profiling/storage_explorer/indices_storage_details?*').as( diff --git a/x-pack/plugins/observability_solution/profiling/public/views/storage_explorer/index.tsx b/x-pack/plugins/observability_solution/profiling/public/views/storage_explorer/index.tsx index 8969a389df01d..ff111c4bebcd0 100644 --- a/x-pack/plugins/observability_solution/profiling/public/views/storage_explorer/index.tsx +++ b/x-pack/plugins/observability_solution/profiling/public/views/storage_explorer/index.tsx @@ -30,7 +30,7 @@ import { Summary } from './summary'; export function StorageExplorerView() { const { query } = useProfilingParams('/storage-explorer'); - const { rangeFrom, rangeTo, kuery, indexLifecyclePhase } = query; + const { rangeFrom, rangeTo, indexLifecyclePhase } = query; const timeRange = useTimeRange({ rangeFrom, rangeTo }); const [selectedTab, setSelectedTab] = useState<'host_breakdown' | 'data_breakdown'>( @@ -47,7 +47,7 @@ export function StorageExplorerView() { http, timeFrom: timeRange.inSeconds.start, timeTo: timeRange.inSeconds.end, - kuery, + kuery: '', indexLifecyclePhase, }); }, @@ -55,7 +55,6 @@ export function StorageExplorerView() { fetchStorageExplorerSummary, timeRange.inSeconds.start, timeRange.inSeconds.end, - kuery, indexLifecyclePhase, ] ); diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/synthetics_service/install_index_templates.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/synthetics_service/install_index_templates.ts index c9ee257335c99..cfbfef34f16da 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/synthetics_service/install_index_templates.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/synthetics_service/install_index_templates.ts @@ -27,9 +27,9 @@ export async function installSyntheticsIndexTemplates(server: SyntheticsServerSe pkgName: 'synthetics', }); - if (!installation) { + if (!installation.package) { return Promise.reject('No package installation found.'); } - return installation; + return installation.package; } diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.test.ts b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.test.ts index da3a25a5fc9df..734b018a6b624 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.test.ts +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.test.ts @@ -4,10 +4,39 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { addUpdatedField } from './use_filter_update'; +import { renderHook } from '@testing-library/react-hooks'; +import { addUpdatedField, useFilterUpdate } from './use_filter_update'; +import * as params from './use_url_params'; describe('useFilterUpdate', () => { + describe('useFilterUpdate hook', () => { + let getUrlParamsSpy; + let updateUrlSpy: jest.Mock; + + beforeEach(() => { + getUrlParamsSpy = jest.fn().mockReturnValue({ + filters: '[["testField",["tag1"]]]', + excludedFilters: '[["testField",["tag2"]]]', + }); + updateUrlSpy = jest.fn(); + jest.spyOn(params, 'useUrlParams').mockReturnValue([getUrlParamsSpy, updateUrlSpy]); + }); + + it('does not update url when filters have not been updated', () => { + renderHook(() => useFilterUpdate('testField', ['tag1'], ['tag2'])); + expect(updateUrlSpy).not.toBeCalled(); + }); + + it('does update url when filters have been updated', () => { + renderHook(() => useFilterUpdate('testField', ['tag1', 'tag2'], [])); + expect(updateUrlSpy).toBeCalledWith({ + filters: '[["testField",["tag1","tag2"]]]', + excludedFilters: '', + pagination: '', + }); + }); + }); + describe('addUpdatedField', () => { it('conditionally adds fields if they are new', () => { const testVal = {}; diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.ts b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.ts index 5578230ab2cf0..498cdcda93f3b 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.ts +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/hooks/use_filter_update.ts @@ -62,6 +62,11 @@ export const useFilterUpdate = ( const newFiltersString = getUpdateFilters(currentFiltersMap, fieldName, values); const newExclusionsString = getUpdateFilters(currentExclusionsMap, fieldName, notValues); + // no new filters to apply + if (filters === newFiltersString && excludedFilters === newExclusionsString) { + return; + } + const update: { [key: string]: string } = {}; addUpdatedField(filters, 'filters', newFiltersString, update); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts index 386093b971c8c..809a3e3ab0598 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_linked_apps.cy.ts @@ -18,7 +18,8 @@ import { import { closeModalIfVisible, closeToastIfVisible } from '../../tasks/integrations'; import { RESULTS_TABLE, RESULTS_TABLE_BUTTON } from '../../screens/live_query'; -describe( +// Failing: See https://github.com/elastic/kibana/issues/173451 +describe.skip( 'Alert Event Details', { tags: ['@ess', '@serverless'], diff --git a/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts index 069753f96fa9a..502e591bbd1a2 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/ecs_mappings.cy.ts @@ -19,7 +19,8 @@ import { } from '../../tasks/live_query'; import { ServerlessRoleName } from '../../support/roles'; -describe('EcsMapping', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/182467 +describe.skip('EcsMapping', { tags: ['@ess', '@serverless'] }, () => { before(() => { initializeDataViews(); }); diff --git a/x-pack/plugins/osquery/cypress/e2e/all/live_query_packs.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/live_query_packs.cy.ts index 85ae94dd3266d..a8461dd9742a8 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/live_query_packs.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/live_query_packs.cy.ts @@ -18,7 +18,8 @@ import { LIVE_QUERY_EDITOR } from '../../screens/live_query'; import { loadPack, cleanupPack, cleanupCase, loadCase } from '../../tasks/api_fixtures'; import { ServerlessRoleName } from '../../support/roles'; -describe('ALL - Live Query Packs', { tags: ['@ess', '@serverless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/169888 +describe.skip('ALL - Live Query Packs', { tags: ['@ess', '@serverless'] }, () => { let packName: string; let packId: string; let caseId: string; diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/constants.ts b/x-pack/plugins/reporting/server/routes/common/jobs/constants.ts new file mode 100644 index 0000000000000..13c2c7ee50bac --- /dev/null +++ b/x-pack/plugins/reporting/server/routes/common/jobs/constants.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const STATUS_CODES = { + COMPLETED: 200, + PENDING: { + INTERNAL: 202, + PUBLIC: 503, + }, + FAILED: { + INTERNAL: 202, + PUBLIC: 500, + }, +}; diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.test.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.test.ts index cbd5f93425847..12de35cd9f32d 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.test.ts @@ -13,8 +13,10 @@ import { CSV_JOB_TYPE } from '@kbn/reporting-export-types-csv-common'; import { PDF_JOB_TYPE, PDF_JOB_TYPE_V2 } from '@kbn/reporting-export-types-pdf-common'; import { createMockConfigSchema } from '@kbn/reporting-mocks-server'; +import { ReportingCore } from '../../..'; import { ContentStream, getContentStream } from '../../../lib'; import { createMockReportingCore } from '../../../test_helpers'; +import { STATUS_CODES } from './constants'; import { getDocumentPayloadFactory } from './get_document_payload'; import { jobsQueryFactory } from './jobs_query'; @@ -22,13 +24,14 @@ jest.mock('../../../lib/content_stream'); jest.mock('./jobs_query'); describe('getDocumentPayload', () => { + let core: ReportingCore; let getDocumentPayload: ReturnType<typeof getDocumentPayloadFactory>; beforeEach(async () => { const schema = createMockConfigSchema(); - const core = await createMockReportingCore(schema); + core = await createMockReportingCore(schema); - getDocumentPayload = getDocumentPayloadFactory(core); + getDocumentPayload = getDocumentPayloadFactory(core, { isInternal: false }); (getContentStream as jest.MockedFunction<typeof getContentStream>).mockResolvedValue( new Readable({ @@ -66,7 +69,7 @@ describe('getDocumentPayload', () => { headers: expect.objectContaining({ 'Content-Length': '1024', }), - statusCode: 200, + statusCode: STATUS_CODES.COMPLETED, }) ); }); @@ -96,57 +99,115 @@ describe('getDocumentPayload', () => { 'kbn-csv-contains-formulas': true, 'kbn-max-size-reached': true, }), - statusCode: 200, + statusCode: STATUS_CODES.COMPLETED, }) ); }); }); - describe('when the report is failed', () => { - it('should return payload for the failed report', async () => { - await expect( - getDocumentPayload({ - id: 'id1', - index: '.reporting-12345', - status: JOB_STATUS.FAILED, - jobtype: PDF_JOB_TYPE_V2, - output: {}, - payload: {}, - } as ReportApiJSON) - ).resolves.toEqual( - expect.objectContaining({ - contentType: 'application/json', - content: { - message: expect.stringContaining('Some error'), - }, - headers: {}, - statusCode: 500, - }) - ); + describe('public API behavior', () => { + beforeEach(() => { + getDocumentPayload = getDocumentPayloadFactory(core, { isInternal: false }); + }); + + describe('when the report is failed', () => { + it('should return payload for the failed report', async () => { + await expect( + getDocumentPayload({ + id: 'id1', + index: '.reporting-12345', + status: JOB_STATUS.FAILED, + jobtype: PDF_JOB_TYPE_V2, + output: {}, + payload: {}, + } as ReportApiJSON) + ).resolves.toEqual( + expect.objectContaining({ + contentType: 'application/json', + content: { + message: expect.stringContaining('Some error'), + }, + headers: {}, + statusCode: STATUS_CODES.FAILED.PUBLIC, + }) + ); + }); + }); + + describe('when the report is incomplete', () => { + it('should return payload for the pending report', async () => { + await expect( + getDocumentPayload({ + id: 'id1', + index: '.reporting-12345', + status: JOB_STATUS.PENDING, + jobtype: PDF_JOB_TYPE_V2, + output: {}, + payload: {}, + } as ReportApiJSON) + ).resolves.toEqual( + expect.objectContaining({ + contentType: 'text/plain', + content: 'pending', + headers: { + 'retry-after': '30', + }, + statusCode: STATUS_CODES.PENDING.PUBLIC, + }) + ); + }); }); }); - describe('when the report is incomplete', () => { - it('should return payload for the pending report', async () => { - await expect( - getDocumentPayload({ - id: 'id1', - index: '.reporting-12345', - status: JOB_STATUS.PENDING, - jobtype: PDF_JOB_TYPE_V2, - output: {}, - payload: {}, - } as ReportApiJSON) - ).resolves.toEqual( - expect.objectContaining({ - contentType: 'text/plain', - content: 'pending', - headers: { - 'retry-after': '30', - }, - statusCode: 503, - }) - ); + describe('internal API behavior', () => { + beforeEach(() => { + getDocumentPayload = getDocumentPayloadFactory(core, { isInternal: true }); + }); + + describe('when the report is failed', () => { + it('should return payload for the failed report', async () => { + await expect( + getDocumentPayload({ + id: 'id1', + index: '.reporting-12345', + status: JOB_STATUS.FAILED, + jobtype: PDF_JOB_TYPE_V2, + output: {}, + payload: {}, + } as ReportApiJSON) + ).resolves.toEqual( + expect.objectContaining({ + contentType: 'application/json', + content: { + message: expect.stringContaining('Some error'), + }, + headers: {}, + statusCode: STATUS_CODES.FAILED.INTERNAL, + }) + ); + }); + }); + + describe('when the report is incomplete', () => { + it('should return payload for the pending report', async () => { + await expect( + getDocumentPayload({ + id: 'id1', + index: '.reporting-12345', + status: JOB_STATUS.PENDING, + jobtype: PDF_JOB_TYPE_V2, + output: {}, + payload: {}, + } as ReportApiJSON) + ).resolves.toEqual( + expect.objectContaining({ + contentType: 'text/plain', + content: 'pending', + headers: { 'retry-after': '30' }, + statusCode: STATUS_CODES.PENDING.INTERNAL, + }) + ); + }); }); }); }); diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.ts index c77e04ec66c22..2a20fa0b475c4 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_document_payload.ts @@ -8,13 +8,14 @@ import { Stream } from 'stream'; import { ResponseHeaders } from '@kbn/core-http-server'; +import { JOB_STATUS } from '@kbn/reporting-common'; import { ReportApiJSON } from '@kbn/reporting-common/types'; import { CSV_JOB_TYPE, CSV_JOB_TYPE_DEPRECATED } from '@kbn/reporting-export-types-csv-common'; -import { JOB_STATUS } from '@kbn/reporting-common'; import { ExportType } from '@kbn/reporting-server'; import { ReportingCore } from '../../..'; import { getContentStream } from '../../../lib'; +import { STATUS_CODES } from './constants'; import { jobsQueryFactory } from './jobs_query'; export interface ErrorFromPayload { @@ -53,7 +54,10 @@ const getReportingHeaders = (output: TaskRunResult, exportType: ExportType) => { return metaDataHeaders; }; -export function getDocumentPayloadFactory(reporting: ReportingCore) { +export function getDocumentPayloadFactory( + reporting: ReportingCore, + { isInternal }: { isInternal: boolean } +) { const { logger: _logger } = reporting.getPluginSetupDeps(); const logger = _logger.get('download-report'); const exportTypesRegistry = reporting.getExportTypesRegistry(); @@ -75,7 +79,7 @@ export function getDocumentPayloadFactory(reporting: ReportingCore) { return { filename, content, - statusCode: 200, + statusCode: STATUS_CODES.COMPLETED, contentType, headers: { ...headers, @@ -84,29 +88,29 @@ export function getDocumentPayloadFactory(reporting: ReportingCore) { }; } - // @TODO: These should be semantic HTTP codes as 500/503's indicate - // error then these are really operating properly. async function getFailure({ id }: ReportApiJSON): Promise<Payload> { - const jobsQuery = jobsQueryFactory(reporting); + const jobsQuery = jobsQueryFactory(reporting, { isInternal }); const error = await jobsQuery.getError(id); - logger.debug(`Report job ${id} has failed. Sending statusCode: 500`); + // For download requested over public API, status code for failed job must be 500 to integrate with Watcher + const statusCode = isInternal ? STATUS_CODES.FAILED.INTERNAL : STATUS_CODES.FAILED.PUBLIC; + logger.debug(`Report job ${id} has failed. Sending statusCode: ${statusCode}`); return { - statusCode: 500, - content: { - message: `Reporting generation failed: ${error}`, - }, + statusCode, + content: { message: `Reporting generation failed: ${error}` }, contentType: 'application/json', headers: {}, }; } function getIncomplete({ id, status }: ReportApiJSON): Payload { - logger.debug(`Report job ${id} is processing. Sending statusCode: 503`); + // For download requested over public API, status code for processing/pending job must be 503 to integrate with Watcher + const statusCode = isInternal ? STATUS_CODES.PENDING.INTERNAL : STATUS_CODES.PENDING.PUBLIC; + logger.debug(`Report job ${id} is processing. Sending statusCode: ${statusCode}`); return { - statusCode: 503, + statusCode, content: status, contentType: 'text/plain', headers: { 'retry-after': '30' }, @@ -124,7 +128,6 @@ export function getDocumentPayloadFactory(reporting: ReportingCore) { } } - // send a 503 indicating that the report isn't completed yet return getIncomplete(report); }; } diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts index 64a3b0224ad71..dc01a29db780a 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/get_job_routes.ts @@ -30,8 +30,11 @@ interface HandlerOpts { res: KibanaResponseFactory; } -export const commonJobsRouteHandlerFactory = (reporting: ReportingCore) => { - const jobsQuery = jobsQueryFactory(reporting); +export const commonJobsRouteHandlerFactory = ( + reporting: ReportingCore, + { isInternal }: { isInternal: boolean } +) => { + const jobsQuery = jobsQueryFactory(reporting, { isInternal }); const handleDownloadReport = ({ path, user, context, req, res }: HandlerOpts) => { const counters = getCounters(req.route.method, path, reporting.getUsageCounter()); @@ -43,36 +46,48 @@ export const commonJobsRouteHandlerFactory = (reporting: ReportingCore) => { const { docId } = req.params; - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const payload = await jobsQuery.getDocumentPayload(doc); - const { contentType, content, filename, statusCode } = payload; - - if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { - return res.badRequest({ - body: `Unsupported content-type of ${contentType} specified by job output`, - }); - } - - const body = typeof content === 'string' ? Buffer.from(content) : content; - - const headers = { - ...payload.headers, - 'content-type': contentType, - }; - - if (filename) { - // event tracking of the downloaded file, if - // the report job was completed successfully - // and a file is available - const eventTracker = reporting.getEventTracker(docId, doc.jobtype, doc.payload.objectType); - const timeSinceCreation = Date.now() - new Date(doc.created_at).valueOf(); - eventTracker?.downloadReport({ timeSinceCreation }); - - return res.file({ body, headers, filename }); + return jobManagementPreRouting( + reporting, + res, + docId, + user, + counters, + { isInternal }, + async (doc) => { + const payload = await jobsQuery.getDocumentPayload(doc); + const { contentType, content, filename, statusCode } = payload; + + if (!contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(contentType)) { + return res.badRequest({ + body: `Unsupported content-type of ${contentType} specified by job output`, + }); + } + + const body = typeof content === 'string' ? Buffer.from(content) : content; + + const headers = { + ...payload.headers, + 'content-type': contentType, + }; + + if (filename) { + // event tracking of the downloaded file, if + // the report job was completed successfully + // and a file is available + const eventTracker = reporting.getEventTracker( + docId, + doc.jobtype, + doc.payload.objectType + ); + const timeSinceCreation = Date.now() - new Date(doc.created_at).valueOf(); + eventTracker?.downloadReport({ timeSinceCreation }); + + return res.file({ body, headers, filename }); + } + + return res.custom({ body, headers, statusCode }); } - - return res.custom({ body, headers, statusCode }); - }); + ); }; const handleDeleteReport = ({ path, user, context, req, res }: HandlerOpts) => { @@ -85,52 +100,64 @@ export const commonJobsRouteHandlerFactory = (reporting: ReportingCore) => { const { docId } = req.params; - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => { - const docIndex = doc.index; - const stream = await getContentStream(reporting, { id: docId, index: docIndex }); - const reportingSetup = reporting.getPluginSetupDeps(); - const logger = reportingSetup.logger.get('delete-report'); - - // An "error" event is emitted if an error is - // passed to the `stream.end` callback from - // the _final method of the ContentStream. - // This event must be handled. - stream.on('error', (err) => { - logger.error(err); - }); - - try { - // Overwriting existing content with an - // empty buffer to remove all the chunks. - await new Promise<void>((resolve, reject) => { - stream.end('', 'utf8', (error?: Error) => { - if (error) { - // handle error that could be thrown - // from the _write method of the ContentStream - reject(error); - } else { - resolve(); - } - }); + return jobManagementPreRouting( + reporting, + res, + docId, + user, + counters, + { isInternal }, + async (doc) => { + const docIndex = doc.index; + const stream = await getContentStream(reporting, { id: docId, index: docIndex }); + const reportingSetup = reporting.getPluginSetupDeps(); + const logger = reportingSetup.logger.get('delete-report'); + + // An "error" event is emitted if an error is + // passed to the `stream.end` callback from + // the _final method of the ContentStream. + // This event must be handled. + stream.on('error', (err) => { + logger.error(err); }); - await jobsQuery.delete(docIndex, docId); + try { + // Overwriting existing content with an + // empty buffer to remove all the chunks. + await new Promise<void>((resolve, reject) => { + stream.end('', 'utf8', (error?: Error) => { + if (error) { + // handle error that could be thrown + // from the _write method of the ContentStream + reject(error); + } else { + resolve(); + } + }); + }); - // event tracking of the deleted report - const eventTracker = reporting.getEventTracker(docId, doc.jobtype, doc.payload.objectType); - const timeSinceCreation = Date.now() - new Date(doc.created_at).valueOf(); - eventTracker?.deleteReport({ timeSinceCreation }); + await jobsQuery.delete(docIndex, docId); - return res.ok({ - body: { deleted: true }, - }); - } catch (error) { - logger.error(error); - return res.customError({ - statusCode: 500, - }); + // event tracking of the deleted report + const eventTracker = reporting.getEventTracker( + docId, + doc.jobtype, + doc.payload.objectType + ); + const timeSinceCreation = Date.now() - new Date(doc.created_at).valueOf(); + eventTracker?.deleteReport({ timeSinceCreation }); + + return res.ok({ + body: { deleted: true }, + }); + } catch (error) { + logger.error(error); + return res.customError({ + statusCode: 500, + }); + } } - }); + ); }; return { diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.test.ts b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.test.ts index cc06ef2e0826c..2f796fd83fbe0 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.test.ts @@ -31,6 +31,7 @@ const mockCounters = { errorCounter: jest.fn(), }; const mockUser = { username: 'joeuser' }; +const options = { isInternal: false }; beforeEach(async () => { mockSetupDeps = createMockPluginSetup({ @@ -70,6 +71,7 @@ it(`should return 404 if the docId isn't resolve`, async function () { 'doc123', mockUser, mockCounters, + options, handler ); @@ -97,6 +99,7 @@ it(`should return forbidden if job type is unrecognized`, async function () { 'doc123', mockUser, mockCounters, + options, handler ); @@ -124,6 +127,7 @@ it(`should call callback when document is available`, async function () { 'doc123', mockUser, mockCounters, + options, handler ); @@ -154,6 +158,7 @@ describe('usage counters', () => { 'doc123', mockUser, mockCounters, + options, handler ); @@ -177,6 +182,7 @@ describe('usage counters', () => { 'doc123', mockUser, mockCounters, + options, handler ); diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts index e9e89c61289b7..e42874468cccf 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/job_management_pre_routing.ts @@ -29,6 +29,7 @@ export const jobManagementPreRouting = async ( jobId: JobId, user: ReportingUser, counters: Counters, + { isInternal }: { isInternal: boolean }, cb: JobManagementResponseHandler ) => { const licenseInfo = await reporting.getLicenseInfo(); @@ -36,7 +37,7 @@ export const jobManagementPreRouting = async ( management: { jobTypes = [] }, } = licenseInfo; - const jobsQuery = jobsQueryFactory(reporting); + const jobsQuery = jobsQueryFactory(reporting, { isInternal }); const doc = await jobsQuery.get(user, jobId); if (!doc) { diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.test.ts b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.test.ts index 616079c0aa27e..8875c7eb874c7 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.test.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.test.ts @@ -23,7 +23,7 @@ describe('jobsQuery', () => { const core = await createMockReportingCore(schema); client = (await core.getEsClient()).asInternalUser as typeof client; - jobsQuery = jobsQueryFactory(core); + jobsQuery = jobsQueryFactory(core, { isInternal: false }); }); describe('list', () => { diff --git a/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts index 56b0ac4677449..3d602bf81f44f 100644 --- a/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts +++ b/x-pack/plugins/reporting/server/routes/common/jobs/jobs_query.ts @@ -53,7 +53,10 @@ export interface JobsQueryFactory { delete(deleteIndex: string, id: string): Promise<TransportResult<estypes.DeleteResponse>>; } -export function jobsQueryFactory(reportingCore: ReportingCore): JobsQueryFactory { +export function jobsQueryFactory( + reportingCore: ReportingCore, + { isInternal }: { isInternal: boolean } +): JobsQueryFactory { async function execQuery< T extends (client: ElasticsearchClient) => Promise<Awaited<ReturnType<T>> | undefined> >(callback: T): Promise<Awaited<ReturnType<T>> | undefined> { @@ -202,7 +205,7 @@ export function jobsQueryFactory(reportingCore: ReportingCore): JobsQueryFactory }, async getDocumentPayload(doc: ReportApiJSON) { - const getDocumentPayload = getDocumentPayloadFactory(reportingCore); + const getDocumentPayload = getDocumentPayloadFactory(reportingCore, { isInternal }); return await getDocumentPayload(doc); }, diff --git a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts index e145598eb7d76..143922d2cfedd 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/integration_tests/jobs.test.ts @@ -34,6 +34,7 @@ import { } from '../../../../test_helpers'; import { ReportingRequestHandlerContext } from '../../../../types'; import { EventTracker } from '../../../../usage'; +import { STATUS_CODES } from '../../../common/jobs/constants'; import { registerJobInfoRoutesInternal as registerJobInfoRoutes } from '../jobs'; type SetupServerReturn = Awaited<ReturnType<typeof setupServer>>; @@ -254,7 +255,7 @@ describe(`Reporting Job Management Routes: Internal`, () => { .expect(403); }); - it('when a job is incomplete', async () => { + it('when a job is incomplete, "internal" API endpoint should return appropriate response', async () => { mockEsClient.search.mockResponseOnce( getHits({ jobtype: mockJobTypeUnencoded, @@ -267,13 +268,13 @@ describe(`Reporting Job Management Routes: Internal`, () => { await server.start(); await supertest(httpSetup.server.listener) .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) - .expect(503) + .expect(STATUS_CODES.PENDING.INTERNAL) .expect('Content-Type', 'text/plain; charset=utf-8') .expect('Retry-After', '30') .then(({ text }) => expect(text).toEqual('pending')); }); - it('when a job fails', async () => { + it('when a job fails, "internal" API endpoint should return appropriate response', async () => { mockEsClient.search.mockResponse( getHits({ jobtype: mockJobTypeUnencoded, @@ -287,7 +288,7 @@ describe(`Reporting Job Management Routes: Internal`, () => { await server.start(); await supertest(httpSetup.server.listener) .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) - .expect(500) + .expect(STATUS_CODES.FAILED.INTERNAL) .expect('Content-Type', 'application/json; charset=utf-8') .then(({ body }) => expect(body.message).toEqual('Reporting generation failed: job failure message') @@ -301,7 +302,7 @@ describe(`Reporting Job Management Routes: Internal`, () => { await server.start(); await supertest(httpSetup.server.listener) .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) - .expect(200) + .expect(STATUS_CODES.COMPLETED) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); }); @@ -318,7 +319,7 @@ describe(`Reporting Job Management Routes: Internal`, () => { await supertest(httpSetup.server.listener) .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dope`) - .expect(200) + .expect(STATUS_CODES.COMPLETED) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); }); @@ -334,7 +335,7 @@ describe(`Reporting Job Management Routes: Internal`, () => { await server.start(); await supertest(httpSetup.server.listener) .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) - .expect(200) + .expect(STATUS_CODES.COMPLETED) .expect('Content-Type', 'text/csv; charset=utf-8') .then(({ text }) => expect(text).toEqual('test')); }); @@ -373,7 +374,7 @@ describe(`Reporting Job Management Routes: Internal`, () => { await server.start(); await supertest(httpSetup.server.listener) .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/japanese-dashboard`) - .expect(200) + .expect(STATUS_CODES.COMPLETED) .expect('Content-Type', 'application/pdf') .expect( 'content-disposition', @@ -446,7 +447,7 @@ describe(`Reporting Job Management Routes: Internal`, () => { await server.start(); await supertest(httpSetup.server.listener) .get(`${INTERNAL_ROUTES.JOBS.DOWNLOAD_PREFIX}/dank`) - .expect(200) + .expect(STATUS_CODES.COMPLETED) .expect('Content-Type', 'text/csv; charset=utf-8') .expect('content-disposition', 'attachment; filename=report.csv'); diff --git a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts index 44a085a76dd91..21e7b92cc4b8f 100644 --- a/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/internal/management/jobs.ts @@ -22,7 +22,7 @@ const { JOBS } = INTERNAL_ROUTES; export function registerJobInfoRoutesInternal(reporting: ReportingCore) { const setupDeps = reporting.getPluginSetupDeps(); const { router } = setupDeps; - const jobsQuery = jobsQueryFactory(reporting); + const jobsQuery = jobsQueryFactory(reporting, { isInternal: true }); const registerInternalGetList = () => { // list jobs in the queue, paginated @@ -105,7 +105,7 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { }; // use common route handlers that are shared for public and internal routes - const jobHandlers = commonJobsRouteHandlerFactory(reporting); + const jobHandlers = commonJobsRouteHandlerFactory(reporting, { isInternal: true }); const registerInternalGetInfo = () => { // return some info about the job @@ -126,13 +126,20 @@ export function registerJobInfoRoutesInternal(reporting: ReportingCore) { } const { docId } = req.params; - return jobManagementPreRouting(reporting, res, docId, user, counters, async (doc) => - res.ok({ - body: doc, - headers: { - 'content-type': 'application/json', - }, - }) + return jobManagementPreRouting( + reporting, + res, + docId, + user, + counters, + { isInternal: true }, + async (doc) => + res.ok({ + body: doc, + headers: { + 'content-type': 'application/json', + }, + }) ); }) ); diff --git a/x-pack/plugins/reporting/server/routes/public/jobs.ts b/x-pack/plugins/reporting/server/routes/public/jobs.ts index 54ebf3d4e0c1c..04d417c4eb89f 100644 --- a/x-pack/plugins/reporting/server/routes/public/jobs.ts +++ b/x-pack/plugins/reporting/server/routes/public/jobs.ts @@ -16,7 +16,7 @@ export function registerJobInfoRoutesPublic(reporting: ReportingCore) { const { router } = setupDeps; // use common route handlers that are shared for public and internal routes - const jobHandlers = commonJobsRouteHandlerFactory(reporting); + const jobHandlers = commonJobsRouteHandlerFactory(reporting, { isInternal: false }); const registerPublicDownloadReport = () => { // trigger a download of the output from a job diff --git a/x-pack/plugins/search_playground/public/components/message_list/assistant_message.test.tsx b/x-pack/plugins/search_playground/public/components/message_list/assistant_message.test.tsx index f3a903331cf3b..4608546616a51 100644 --- a/x-pack/plugins/search_playground/public/components/message_list/assistant_message.test.tsx +++ b/x-pack/plugins/search_playground/public/components/message_list/assistant_message.test.tsx @@ -38,7 +38,7 @@ describe('AssistantMessage component', () => { createdAt: new Date(), citations: [], retrievalDocs: [{ content: '', metadata: { _id: '1', _index: 'index', _score: 1 } }], - inputTokens: { context: 20, total: 10 }, + inputTokens: { context: 20, total: 10, searchQuery: 'Test question' }, }; it('renders message content correctly', () => { diff --git a/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx b/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx index 7e94059bdc272..e0b14c2a31934 100644 --- a/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx +++ b/x-pack/plugins/search_playground/public/components/message_list/assistant_message.tsx @@ -53,53 +53,79 @@ export const AssistantMessage: React.FC<AssistantMessageProps> = ({ message }) = return ( <> {!!retrievalDocs?.length && ( - <EuiComment - username={username} - timelineAvatar="dot" - data-test-subj="retrieval-docs-comment" - eventColor="subdued" - css={{ - '.euiAvatar': { backgroundColor: euiTheme.colors.ghost }, - '.euiCommentEvent': { - border: euiTheme.border.thin, - borderRadius: euiTheme.border.radius.medium, - }, - }} - event={ - <> + <> + <EuiComment + username={username} + timelineAvatar="dot" + data-test-subj="assistant-message-searching" + eventColor="subdued" + css={{ + '.euiAvatar': { backgroundColor: euiTheme.colors.ghost }, + '.euiCommentEvent': { + border: euiTheme.border.thin, + borderRadius: euiTheme.border.radius.medium, + }, + }} + event={ <EuiText size="s"> <p> <FormattedMessage - id="xpack.searchPlayground.chat.message.assistant.retrievalDocs" - defaultMessage="Grounding answer based on" + id="xpack.searchPlayground.chat.message.assistant.searchingQuestion" + defaultMessage='Searching for "{question}"' + values={{ question: inputTokens.searchQuery }} /> - {` `} </p> </EuiText> + } + /> + <EuiComment + username={username} + timelineAvatar="dot" + data-test-subj="retrieval-docs-comment" + eventColor="subdued" + css={{ + '.euiAvatar': { backgroundColor: euiTheme.colors.ghost }, + '.euiCommentEvent': { + border: euiTheme.border.thin, + borderRadius: euiTheme.border.radius.medium, + }, + }} + event={ + <> + <EuiText size="s"> + <p> + <FormattedMessage + id="xpack.searchPlayground.chat.message.assistant.retrievalDocs" + defaultMessage="Grounding answer based on" + /> + {` `} + </p> + </EuiText> - <EuiButtonEmpty - css={{ blockSize: 'auto' }} - size="s" - flush="left" - data-test-subj="retrieval-docs-button" - onClick={() => setIsDocsFlyoutOpen(true)} - > - <FormattedMessage - id="xpack.searchPlayground.chat.message.assistant.retrievalDocButton" - defaultMessage="{count} document sources" - values={{ count: retrievalDocs.length }} - /> - </EuiButtonEmpty> + <EuiButtonEmpty + css={{ blockSize: 'auto' }} + size="s" + flush="left" + data-test-subj="retrieval-docs-button" + onClick={() => setIsDocsFlyoutOpen(true)} + > + <FormattedMessage + id="xpack.searchPlayground.chat.message.assistant.retrievalDocButton" + defaultMessage="{count} document sources" + values={{ count: retrievalDocs.length }} + /> + </EuiButtonEmpty> - {isDocsFlyoutOpen && ( - <RetrievalDocsFlyout - onClose={() => setIsDocsFlyoutOpen(false)} - retrievalDocs={retrievalDocs} - /> - )} - </> - } - /> + {isDocsFlyoutOpen && ( + <RetrievalDocsFlyout + onClose={() => setIsDocsFlyoutOpen(false)} + retrievalDocs={retrievalDocs} + /> + )} + </> + } + /> + </> )} {retrievalDocs?.length === 0 && ( <EuiComment diff --git a/x-pack/plugins/search_playground/public/hooks/use_ai_assist_chat.ts b/x-pack/plugins/search_playground/public/hooks/use_ai_assist_chat.ts index f29bb7de11f88..d046b9425c60a 100644 --- a/x-pack/plugins/search_playground/public/hooks/use_ai_assist_chat.ts +++ b/x-pack/plugins/search_playground/public/hooks/use_ai_assist_chat.ts @@ -184,7 +184,7 @@ export function useAIAssistChat({ if (messagesRef.current.length === 0) return null; const chatRequest: ChatRequest = { - messages: messagesRef.current, + messages: messagesRef.current.slice(0, messagesRef.current.length - 1), options, data, }; diff --git a/x-pack/plugins/search_playground/public/types.ts b/x-pack/plugins/search_playground/public/types.ts index 2bbd45ff16230..90f6cdc72e04f 100644 --- a/x-pack/plugins/search_playground/public/types.ts +++ b/x-pack/plugins/search_playground/public/types.ts @@ -100,8 +100,9 @@ export interface AnnotationDoc { } export interface AnnotationTokens { - type: 'prompt_token_count' | 'context_token_count' | 'context_clipped'; + type: 'prompt_token_count' | 'context_token_count' | 'context_clipped' | 'search_query'; count: number; + question?: string; } export interface Doc { @@ -117,6 +118,7 @@ export interface AIMessage extends Message { context: number; total: number; contextClipped?: number; + searchQuery: string; }; } diff --git a/x-pack/plugins/search_playground/public/utils/transform_to_messages.ts b/x-pack/plugins/search_playground/public/utils/transform_to_messages.ts index 4500ac6f10890..af8d75a8ee7f1 100644 --- a/x-pack/plugins/search_playground/public/utils/transform_to_messages.ts +++ b/x-pack/plugins/search_playground/public/utils/transform_to_messages.ts @@ -44,6 +44,9 @@ export const transformFromChatMessages = (messages: UseChatHelpers['messages']): contextClipped: annotations?.find( (annotation): annotation is AnnotationTokens => annotation.type === 'context_clipped' )?.count, + searchQuery: annotations?.find( + (annotation): annotation is AnnotationTokens => annotation.type === 'search_query' + )?.question, }, } as AIMessage; } diff --git a/x-pack/plugins/search_playground/server/lib/conversational_chain.ts b/x-pack/plugins/search_playground/server/lib/conversational_chain.ts index c63481e93c98f..922f672bda5c6 100644 --- a/x-pack/plugins/search_playground/server/lib/conversational_chain.ts +++ b/x-pack/plugins/search_playground/server/lib/conversational_chain.ts @@ -198,6 +198,13 @@ class ConversationalChainFn { context: RunnableSequence.from([(input) => input.question, retrievalChain]), question: (input) => input.question, }, + RunnableLambda.from((inputs) => { + data.appendMessageAnnotation({ + type: 'search_query', + question: inputs.question, + }); + return inputs; + }), RunnableLambda.from(clipContext(this.options?.rag?.inputTokensLimit, prompt, data)), RunnableLambda.from(registerContextTokenCounts(data)), prompt, @@ -236,6 +243,10 @@ class ConversationalChainFn { type: 'prompt_token_count', count: getTokenEstimateFromMessages(msg), }); + data.appendMessageAnnotation({ + type: 'search_query', + question, + }); } }, // callback for prompt based models (Bedrock uses ActionsClientLlm) diff --git a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx index 8e2f27c2db817..30102964b3438 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx +++ b/x-pack/plugins/security/public/account_management/user_profile/user_profile.tsx @@ -437,7 +437,18 @@ function UserAvatarEditor({ ) } onChange={createImageHandler((imageUrl) => { - formik.setFieldValue('data.avatar.imageUrl', imageUrl ?? ''); + if (!imageUrl) { + formik.setFieldError( + 'data.avatar.imageUrl', + i18n.translate( + 'xpack.security.accountManagement.userProfile.imageUrlRequiredError', + { defaultMessage: 'Upload an image.' } + ) + ); + formik.setFieldTouched('data.avatar.imageUrl', true); + } else { + formik.setFieldValue('data.avatar.imageUrl', imageUrl ?? ''); + } })} validate={{ required: i18n.translate( diff --git a/x-pack/plugins/security/public/account_management/user_profile/utils.ts b/x-pack/plugins/security/public/account_management/user_profile/utils.ts index 2621ba0b65f1f..31580fd7fdfd2 100644 --- a/x-pack/plugins/security/public/account_management/user_profile/utils.ts +++ b/x-pack/plugins/security/public/account_management/user_profile/utils.ts @@ -62,6 +62,8 @@ export function createImageHandler(callback: (imageUrl: string | undefined) => v const imageUrl = await readFile(file); const resizedImageUrl = await resizeImage(imageUrl, MAX_IMAGE_SIZE); callback(resizedImageUrl); + } else { + callback(undefined); } }; } diff --git a/x-pack/plugins/security/server/routes/authorization/roles/index.ts b/x-pack/plugins/security/server/routes/authorization/roles/index.ts index d4e3633f4677e..d5af481ca3c11 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/index.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/index.ts @@ -9,6 +9,7 @@ import { defineDeleteRolesRoutes } from './delete'; import { defineGetRolesRoutes } from './get'; import { defineGetAllRolesRoutes } from './get_all'; import { defineGetAllRolesBySpaceRoutes } from './get_all_by_space'; +import { defineBulkCreateOrUpdateRolesRoutes } from './post'; import { definePutRolesRoutes } from './put'; import type { RouteDefinitionParams } from '../..'; @@ -18,4 +19,5 @@ export function defineRolesRoutes(params: RouteDefinitionParams) { defineDeleteRolesRoutes(params); definePutRolesRoutes(params); defineGetAllRolesBySpaceRoutes(params); + defineBulkCreateOrUpdateRolesRoutes(params); } diff --git a/x-pack/plugins/security/server/routes/authorization/roles/lib/index.ts b/x-pack/plugins/security/server/routes/authorization/roles/lib/index.ts new file mode 100644 index 0000000000000..06318eb2bacd7 --- /dev/null +++ b/x-pack/plugins/security/server/routes/authorization/roles/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor 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 { roleGrantsSubFeaturePrivileges } from './role_privileges'; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/lib/role_privileges.ts b/x-pack/plugins/security/server/routes/authorization/roles/lib/role_privileges.ts new file mode 100644 index 0000000000000..49413b0d22d89 --- /dev/null +++ b/x-pack/plugins/security/server/routes/authorization/roles/lib/role_privileges.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { KibanaFeature } from '@kbn/features-plugin/common'; + +import type { RolePayloadSchemaType } from '../model/put_payload'; + +export const roleGrantsSubFeaturePrivileges = ( + features: KibanaFeature[], + role: RolePayloadSchemaType +) => { + if (!role.kibana) { + return false; + } + + const subFeaturePrivileges = new Map( + features.map((feature) => [ + feature.id, + feature.subFeatures.map((sf) => sf.privilegeGroups.map((pg) => pg.privileges)).flat(2), + ]) + ); + + const hasAnySubFeaturePrivileges = role.kibana.some((kibanaPrivilege) => + Object.entries(kibanaPrivilege.feature ?? {}).some(([featureId, privileges]) => { + return !!subFeaturePrivileges.get(featureId)?.some(({ id }) => privileges.includes(id)); + }) + ); + + return hasAnySubFeaturePrivileges; +}; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/bulk_create_or_update_payload.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/bulk_create_or_update_payload.test.ts new file mode 100644 index 0000000000000..c26bb9ff24793 --- /dev/null +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/bulk_create_or_update_payload.test.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getBulkCreateOrUpdatePayloadSchema } from './bulk_create_or_update_payload'; + +describe('getBulkCreateOrUpdatePayloadSchema', () => { + const mockGetBasePrivilegeNames = jest.fn(() => ({ + global: ['all', 'read'], + space: ['all', 'read'], + })); + + const bulkCreateOrUpdatePayloadSchema = + getBulkCreateOrUpdatePayloadSchema(mockGetBasePrivilegeNames); + + it('should validate a correct payload', () => { + const payload = { + roles: { + role1: { + kibana: [ + { + feature: { + feature1: ['all'], + }, + spaces: ['*'], + }, + ], + }, + }, + }; + + expect(() => bulkCreateOrUpdatePayloadSchema.validate(payload)).not.toThrow(); + }); + + it('should throw an error for missing roles', () => { + const payload = {}; + + expect(() => + bulkCreateOrUpdatePayloadSchema.validate(payload) + ).toThrowErrorMatchingInlineSnapshot( + `"[roles]: expected value of type [object] but got [undefined]"` + ); + }); + + it('should throw an error for invalid role structure', () => { + const payload = { + roles: { + role1: 'invalid_structure', + }, + }; + + expect(() => + bulkCreateOrUpdatePayloadSchema.validate(payload) + ).toThrowErrorMatchingInlineSnapshot( + `"[roles.role1]: could not parse object value from json input"` + ); + }); +}); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/bulk_create_or_update_payload.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/bulk_create_or_update_payload.ts new file mode 100644 index 0000000000000..cfe21261719f9 --- /dev/null +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/bulk_create_or_update_payload.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { TypeOf } from '@kbn/config-schema'; +import { schema } from '@kbn/config-schema'; + +import { getPutPayloadSchema } from './put_payload'; + +export function getBulkCreateOrUpdatePayloadSchema( + getBasePrivilegeNames: () => { global: string[]; space: string[] } +) { + return schema.object({ + roles: schema.recordOf(schema.string(), getPutPayloadSchema(getBasePrivilegeNames)), + }); +} + +export type BulkCreateOrUpdateRolesPayloadSchemaType = TypeOf< + ReturnType<typeof getBulkCreateOrUpdatePayloadSchema> +>; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/index.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/index.ts index f42d6f01573a9..3b9e59f10046b 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/model/index.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/model/index.ts @@ -7,3 +7,6 @@ export type { RolePayloadSchemaType } from './put_payload'; export { transformPutPayloadToElasticsearchRole, getPutPayloadSchema } from './put_payload'; + +export { getBulkCreateOrUpdatePayloadSchema } from './bulk_create_or_update_payload'; +export type { BulkCreateOrUpdateRolesPayloadSchemaType } from './bulk_create_or_update_payload'; diff --git a/x-pack/plugins/security/server/routes/authorization/roles/post.test.ts b/x-pack/plugins/security/server/routes/authorization/roles/post.test.ts new file mode 100644 index 0000000000000..0ef752423606d --- /dev/null +++ b/x-pack/plugins/security/server/routes/authorization/roles/post.test.ts @@ -0,0 +1,999 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { kibanaResponseFactory } from '@kbn/core/server'; +import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; +import { KibanaFeature } from '@kbn/features-plugin/server'; +import type { LicenseCheck } from '@kbn/licensing-plugin/server'; +import { GLOBAL_RESOURCE } from '@kbn/security-plugin-types-server'; + +import type { BulkCreateOrUpdateRolesPayloadSchemaType } from './model/bulk_create_or_update_payload'; +import { defineBulkCreateOrUpdateRolesRoutes } from './post'; +import { securityFeatureUsageServiceMock } from '../../../feature_usage/index.mock'; +import { routeDefinitionParamsMock } from '../../index.mock'; + +const application = 'kibana-.kibana'; +const privilegeMap = { + global: { + all: [], + read: [], + }, + space: { + all: [], + read: [], + }, + features: { + foo: { + 'foo-privilege-1': [], + 'foo-privilege-2': [], + }, + bar: { + 'bar-privilege-1': [], + 'bar-privilege-2': [], + }, + }, + reserved: { + customApplication1: [], + customApplication2: [], + }, +}; + +const kibanaFeature = new KibanaFeature({ + id: 'bar', + name: 'bar', + privileges: { + all: { + requireAllSpaces: true, + savedObject: { + all: [], + read: [], + }, + ui: [], + }, + read: { + disabled: true, + savedObject: { + all: [], + read: [], + }, + ui: [], + }, + }, + app: [], + category: { id: 'bar', label: 'bar' }, +}); + +interface TestOptions { + licenseCheckResult?: LicenseCheck; + apiResponses?: { + get: () => unknown; + post: () => Record<string, unknown>; + }; + payload: BulkCreateOrUpdateRolesPayloadSchemaType; + asserts: { + statusCode: number; + result?: Record<string, any>; + apiArguments?: { get: unknown[]; post: unknown[] }; + recordSubFeaturePrivilegeUsage?: boolean; + }; + features?: KibanaFeature[]; +} + +const postRolesTest = ( + description: string, + { payload, licenseCheckResult = { state: 'valid' }, apiResponses, asserts, features }: TestOptions +) => { + test(description, async () => { + const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + mockRouteDefinitionParams.authz.applicationName = application; + mockRouteDefinitionParams.authz.privileges.get.mockReturnValue(privilegeMap); + + const mockCoreContext = coreMock.createRequestHandlerContext(); + const mockLicensingContext = { + license: { check: jest.fn().mockReturnValue(licenseCheckResult) }, + } as any; + const mockContext = coreMock.createCustomRequestHandlerContext({ + core: mockCoreContext, + licensing: mockLicensingContext, + }); + + if (apiResponses?.get) { + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole.mockResponseImplementationOnce( + (() => ({ body: apiResponses?.get() })) as any + ); + } + + if (apiResponses?.post) { + mockCoreContext.elasticsearch.client.asCurrentUser.transport.request.mockImplementationOnce( + (() => ({ ...apiResponses?.post() })) as any + ); + } + + mockRouteDefinitionParams.getFeatureUsageService.mockReturnValue( + securityFeatureUsageServiceMock.createStartContract() + ); + + mockRouteDefinitionParams.getFeatures.mockResolvedValue( + features ?? [ + new KibanaFeature({ + id: 'feature_1', + name: 'feature 1', + app: [], + category: { id: 'foo', label: 'foo' }, + privileges: { + all: { + ui: [], + savedObject: { all: [], read: [] }, + }, + read: { + ui: [], + savedObject: { all: [], read: [] }, + }, + }, + subFeatures: [ + { + name: 'sub feature 1', + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'sub_feature_privilege_1', + name: 'first sub-feature privilege', + includeIn: 'none', + ui: [], + savedObject: { all: [], read: [] }, + }, + ], + }, + ], + }, + ], + }), + ] + ); + + defineBulkCreateOrUpdateRolesRoutes(mockRouteDefinitionParams); + const [[{ validate }, handler]] = mockRouteDefinitionParams.router.post.mock.calls; + + const headers = { authorization: 'foo' }; + const mockRequest = httpServerMock.createKibanaRequest({ + method: 'post', + path: '/api/security/roles', + body: payload !== undefined ? (validate as any).body.validate(payload) : undefined, + headers, + }); + + const response = await handler(mockContext, mockRequest, kibanaResponseFactory); + expect(response.status).toBe(asserts.statusCode); + expect(response.payload).toEqual(asserts.result); + + if (asserts.apiArguments?.get) { + expect( + mockCoreContext.elasticsearch.client.asCurrentUser.security.getRole + ).toHaveBeenCalledWith(...asserts.apiArguments?.get); + } + if (asserts.apiArguments?.post) { + const [body] = asserts.apiArguments?.post ?? []; + expect( + mockCoreContext.elasticsearch.client.asCurrentUser.transport.request + ).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/role', + body, + }); + } + expect(mockLicensingContext.license.check).toHaveBeenCalledWith('security', 'basic'); + + if (asserts.recordSubFeaturePrivilegeUsage) { + expect( + mockRouteDefinitionParams.getFeatureUsageService().recordSubFeaturePrivilegeUsage + ).toHaveBeenCalledTimes( + (response.payload?.created?.length ?? 0) + + (response.payload?.updated?.length ?? 0) + + (response.payload?.noop?.length ?? 0) + ); + } else { + expect( + mockRouteDefinitionParams.getFeatureUsageService().recordSubFeaturePrivilegeUsage + ).not.toHaveBeenCalled(); + } + }); +}; + +describe('POST roles', () => { + describe('failure', () => { + postRolesTest('returns result of license checker', { + payload: { roles: {} } as BulkCreateOrUpdateRolesPayloadSchemaType, + licenseCheckResult: { state: 'invalid', message: 'test forbidden message' }, + asserts: { statusCode: 403, result: { message: 'test forbidden message' } }, + }); + }); + + describe('success', () => { + postRolesTest(`creates empty roles`, { + payload: { + roles: { + 'role-1': { + elasticsearch: {}, + kibana: [], + }, + 'role-2': { + elasticsearch: {}, + kibana: [], + }, + }, + }, + apiResponses: { + get: () => ({}), + post: () => ({ created: ['role-1', 'role-2'] }), + }, + asserts: { + apiArguments: { + get: [{ name: 'role-1,role-2' }, { ignore: [404] }], + post: [ + { + roles: { + 'role-1': { + applications: [], + cluster: [], + indices: [], + remote_indices: undefined, + remote_cluster: undefined, + run_as: [], + metadata: undefined, + }, + 'role-2': { + applications: [], + cluster: [], + indices: [], + remote_indices: undefined, + remote_cluster: undefined, + run_as: [], + metadata: undefined, + }, + }, + }, + ], + }, + statusCode: 200, + result: { created: ['role-1', 'role-2'] }, + }, + }); + + postRolesTest('returns validation errors', { + payload: { + roles: { + 'role-1': { + elasticsearch: {}, + kibana: [ + { + spaces: ['bar-space'], + base: [], + feature: { + bar: ['all', 'read'], + }, + }, + ], + }, + }, + }, + apiResponses: { + get: () => ({}), + post: () => ({}), + }, + features: [kibanaFeature], + asserts: { + statusCode: 200, + result: { + errors: { + 'role-1': { + type: 'kibana_privilege_validation_exception', + reason: + 'Role cannot be updated due to validation errors: ["Feature privilege [bar.all] requires all spaces to be selected but received [bar-space]","Feature [bar] does not support privilege [read]."]', + }, + }, + }, + }, + }); + + postRolesTest(`returns errors for not updated/created roles`, { + payload: { + roles: { + 'role-1': { + elasticsearch: {}, + kibana: [ + { + spaces: ['bar-space'], + base: [], + feature: { + bar: ['all', 'read'], + }, + }, + ], + }, + 'role-2': { + elasticsearch: { + indices: [ + { + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test'], + }, + ], + }, + }, + 'role-3': { + elasticsearch: {}, + kibana: [], + }, + }, + }, + features: [kibanaFeature], + apiResponses: { + get: () => ({}), + post: () => ({ + created: ['role-3'], + errors: { + count: 1, + details: { + 'role-2': { + type: 'action_request_validation_exception', + reason: 'Validation Failed', + }, + }, + }, + }), + }, + asserts: { + apiArguments: { + get: [{ name: 'role-2,role-3' }, { ignore: [404] }], + post: [ + { + roles: { + 'role-2': { + applications: [], + cluster: [], + indices: [ + { + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test'], + }, + ], + remote_indices: undefined, + remote_cluster: undefined, + run_as: [], + metadata: undefined, + }, + 'role-3': { + applications: [], + cluster: [], + indices: [], + remote_indices: undefined, + remote_cluster: undefined, + run_as: [], + metadata: undefined, + }, + }, + }, + ], + }, + statusCode: 200, + result: { + created: ['role-3'], + errors: { + 'role-1': { + type: 'kibana_privilege_validation_exception', + reason: + 'Role cannot be updated due to validation errors: ["Feature privilege [bar.all] requires all spaces to be selected but received [bar-space]","Feature [bar] does not support privilege [read]."]', + }, + 'role-2': { + reason: 'Validation Failed', + type: 'action_request_validation_exception', + }, + }, + }, + }, + }); + + postRolesTest( + `creates non-existing role and updates role which has existing kibana privileges`, + { + payload: { + roles: { + 'new-role': { + kibana: [], + elasticsearch: { + remote_cluster: [ + { + clusters: ['cluster1', 'cluster2'], + privileges: ['monitor_enrich'], + }, + { + clusters: ['cluster3', 'cluster4'], + privileges: ['monitor_enrich'], + }, + ], + }, + }, + 'existing-role': { + elasticsearch: { + cluster: ['test-cluster-privilege'], + indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + kibana: [ + { + feature: { + foo: ['foo-privilege-1'], + bar: ['bar-privilege-1'], + }, + spaces: ['*'], + }, + { + base: ['all'], + spaces: ['test-space-1', 'test-space-2'], + }, + { + feature: { + bar: ['bar-privilege-2'], + }, + spaces: ['test-space-3'], + }, + ], + }, + }, + }, + apiResponses: { + get: () => ({ + 'existing-role': { + cluster: ['old-cluster-privilege'], + indices: [ + { + field_security: { + grant: ['old-field-security-grant-1', 'old-field-security-grant-2'], + except: ['old-field-security-except-1', 'old-field-security-except-2'], + }, + names: ['old-index-name'], + privileges: ['old-privilege'], + query: `{ "match": { "old-title": "foo" } }`, + }, + ], + run_as: ['old-run-as'], + applications: [ + { + application, + privileges: ['old-kibana-privilege'], + resources: ['old-resource'], + }, + ], + }, + }), + post: () => ({ updated: ['existing-role'], created: ['new-role'] }), + }, + asserts: { + apiArguments: { + get: [{ name: 'new-role,existing-role' }, { ignore: [404] }], + post: [ + { + roles: { + 'new-role': { + applications: [], + cluster: [], + indices: [], + remote_indices: undefined, + run_as: [], + remote_cluster: [ + { + clusters: ['cluster1', 'cluster2'], + privileges: ['monitor_enrich'], + }, + { + clusters: ['cluster3', 'cluster4'], + privileges: ['monitor_enrich'], + }, + ], + metadata: undefined, + }, + 'existing-role': { + applications: [ + { + application, + privileges: ['feature_foo.foo-privilege-1', 'feature_bar.bar-privilege-1'], + resources: [GLOBAL_RESOURCE], + }, + { + application, + privileges: ['space_all'], + resources: ['space:test-space-1', 'space:test-space-2'], + }, + { + application, + privileges: ['feature_bar.bar-privilege-2'], + resources: ['space:test-space-3'], + }, + ], + cluster: ['test-cluster-privilege'], + indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + remote_indices: undefined, + metadata: undefined, + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + }, + }, + ], + }, + statusCode: 200, + result: { updated: ['existing-role'], created: ['new-role'] }, + }, + } + ); + + postRolesTest(`notifies when sub-feature privileges are included`, { + payload: { + roles: { + 'role-1': { + elasticsearch: {}, + kibana: [ + { + spaces: ['*'], + feature: { + feature_1: ['sub_feature_privilege_1'], + }, + }, + ], + }, + 'role-2': { + elasticsearch: {}, + kibana: [ + { + spaces: ['*'], + feature: { + feature_1: ['sub_feature_privilege_1'], + }, + }, + ], + }, + }, + }, + apiResponses: { + get: () => ({}), + post: () => ({ created: ['role-1', 'role-2'] }), + }, + asserts: { + recordSubFeaturePrivilegeUsage: true, + apiArguments: { + get: [{ name: 'role-1,role-2' }, { ignore: [404] }], + post: [ + { + roles: { + 'role-1': { + cluster: [], + indices: [], + remote_indices: undefined, + run_as: [], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_feature_1.sub_feature_privilege_1'], + resources: ['*'], + }, + ], + metadata: undefined, + }, + 'role-2': { + cluster: [], + indices: [], + remote_indices: undefined, + run_as: [], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_feature_1.sub_feature_privilege_1'], + resources: ['*'], + }, + ], + metadata: undefined, + }, + }, + }, + ], + }, + statusCode: 200, + result: { created: ['role-1', 'role-2'] }, + }, + }); + + postRolesTest(`creates roles with everything`, { + payload: { + roles: { + 'role-1': { + description: 'role 1 test description', + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['test-cluster-privilege'], + indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + remote_indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + clusters: ['test-cluster-name-1', 'test-cluster-name-2'], + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + kibana: [ + { + base: ['all', 'read'], + spaces: ['*'], + }, + { + base: ['all', 'read'], + spaces: ['test-space-1', 'test-space-2'], + }, + { + feature: { + foo: ['foo-privilege-1', 'foo-privilege-2'], + }, + spaces: ['test-space-3'], + }, + ], + }, + 'role-2': { + description: 'role 2 test description', + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['test-cluster-privilege'], + remote_cluster: [ + { + clusters: ['cluster1', 'cluster2'], + privileges: ['monitor_enrich'], + }, + { + clusters: ['cluster3', 'cluster4'], + privileges: ['monitor_enrich'], + }, + ], + indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + remote_indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + clusters: ['test-cluster-name-1', 'test-cluster-name-2'], + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + kibana: [ + { + base: ['all', 'read'], + spaces: ['test-space-1', 'test-space-2'], + }, + { + feature: { + foo: ['foo-privilege-1', 'foo-privilege-2'], + }, + spaces: ['test-space-3'], + }, + ], + }, + }, + }, + apiResponses: { + get: () => ({}), + post: () => ({ created: ['role-1', 'role-2'] }), + }, + asserts: { + apiArguments: { + get: [{ name: 'role-1,role-2' }, { ignore: [404] }], + post: [ + { + roles: { + 'role-1': { + applications: [ + { + application, + privileges: ['all', 'read'], + resources: [GLOBAL_RESOURCE], + }, + { + application, + privileges: ['space_all', 'space_read'], + resources: ['space:test-space-1', 'space:test-space-2'], + }, + { + application, + privileges: ['feature_foo.foo-privilege-1', 'feature_foo.foo-privilege-2'], + resources: ['space:test-space-3'], + }, + ], + cluster: ['test-cluster-privilege'], + description: 'role 1 test description', + indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + remote_indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + clusters: ['test-cluster-name-1', 'test-cluster-name-2'], + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + metadata: { foo: 'test-metadata' }, + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + 'role-2': { + applications: [ + { + application, + privileges: ['space_all', 'space_read'], + resources: ['space:test-space-1', 'space:test-space-2'], + }, + { + application, + privileges: ['feature_foo.foo-privilege-1', 'feature_foo.foo-privilege-2'], + resources: ['space:test-space-3'], + }, + ], + cluster: ['test-cluster-privilege'], + remote_cluster: [ + { + clusters: ['cluster1', 'cluster2'], + privileges: ['monitor_enrich'], + }, + { + clusters: ['cluster3', 'cluster4'], + privileges: ['monitor_enrich'], + }, + ], + description: 'role 2 test description', + indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + remote_indices: [ + { + field_security: { + grant: ['test-field-security-grant-1', 'test-field-security-grant-2'], + except: ['test-field-security-except-1', 'test-field-security-except-2'], + }, + clusters: ['test-cluster-name-1', 'test-cluster-name-2'], + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + query: `{ "match": { "title": "foo" } }`, + }, + ], + metadata: { foo: 'test-metadata' }, + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + }, + }, + ], + }, + statusCode: 200, + result: { created: ['role-1', 'role-2'] }, + }, + }); + + postRolesTest(`updates roles which have existing other application privileges`, { + payload: { + roles: { + 'role-1': { + elasticsearch: { + cluster: ['test-cluster-privilege'], + indices: [ + { + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + kibana: [ + { + base: ['all', 'read'], + spaces: ['*'], + }, + ], + }, + 'role-2': { + elasticsearch: { + cluster: ['test-cluster-privilege'], + indices: [ + { + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + kibana: [ + { + base: ['all', 'read'], + spaces: ['*'], + }, + ], + }, + }, + }, + apiResponses: { + get: () => ({ + 'role-1': { + cluster: ['old-cluster-privilege'], + indices: [], + run_as: ['old-run-as'], + applications: [ + { + application, + privileges: ['old-kibana-privilege'], + resources: ['old-resource'], + }, + { + application: 'logstash-foo', + privileges: ['logstash-privilege'], + resources: ['logstash-resource'], + }, + ], + }, + 'role-2': { + cluster: ['old-cluster-privilege'], + indices: [ + { + names: ['old-index-name'], + privileges: ['old-privilege'], + }, + ], + run_as: ['old-run-as'], + applications: [ + { + application, + privileges: ['old-kibana-privilege'], + resources: ['old-resource'], + }, + { + application: 'beats-foo', + privileges: ['beats-privilege'], + resources: ['beats-resource'], + }, + ], + }, + }), + post: () => ({ updated: ['role-1', 'role-2'] }), + }, + asserts: { + apiArguments: { + get: [{ name: 'role-1,role-2' }, { ignore: [404] }], + post: [ + { + roles: { + 'role-1': { + applications: [ + { + application, + privileges: ['all', 'read'], + resources: [GLOBAL_RESOURCE], + }, + { + application: 'logstash-foo', + privileges: ['logstash-privilege'], + resources: ['logstash-resource'], + }, + ], + cluster: ['test-cluster-privilege'], + indices: [ + { + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + 'role-2': { + applications: [ + { + application, + privileges: ['all', 'read'], + resources: [GLOBAL_RESOURCE], + }, + { + application: 'beats-foo', + privileges: ['beats-privilege'], + resources: ['beats-resource'], + }, + ], + cluster: ['test-cluster-privilege'], + indices: [ + { + names: ['test-index-name-1', 'test-index-name-2'], + privileges: ['test-index-privilege-1', 'test-index-privilege-2'], + }, + ], + run_as: ['test-run-as-1', 'test-run-as-2'], + }, + }, + }, + ], + }, + statusCode: 200, + result: { updated: ['role-1', 'role-2'] }, + }, + }); + }); +}); diff --git a/x-pack/plugins/security/server/routes/authorization/roles/post.ts b/x-pack/plugins/security/server/routes/authorization/roles/post.ts new file mode 100644 index 0000000000000..07b9886c4072c --- /dev/null +++ b/x-pack/plugins/security/server/routes/authorization/roles/post.ts @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { roleGrantsSubFeaturePrivileges } from './lib'; +import { + getBulkCreateOrUpdatePayloadSchema, + transformPutPayloadToElasticsearchRole, +} from './model'; +import type { RouteDefinitionParams } from '../..'; +import { wrapIntoCustomErrorResponse } from '../../../errors'; +import { validateKibanaPrivileges } from '../../../lib'; +import { createLicensedRouteHandler } from '../../licensed_route_handler'; + +type RolesErrorsDetails = Record< + string, + { + type: string; + reason: string; + } +>; + +interface ESRolesResponse { + noop?: string[]; + created?: string[]; + updated?: string[]; + errors?: { + count: number; + details: RolesErrorsDetails; + }; +} + +export function defineBulkCreateOrUpdateRolesRoutes({ + router, + authz, + getFeatures, + getFeatureUsageService, +}: RouteDefinitionParams) { + router.post( + { + path: '/api/security/roles', + options: { + summary: 'Create or update roles', + }, + validate: { + body: getBulkCreateOrUpdatePayloadSchema(() => { + const privileges = authz.privileges.get(); + return { + global: Object.keys(privileges.global), + space: Object.keys(privileges.space), + }; + }), + }, + }, + createLicensedRouteHandler(async (context, request, response) => { + try { + const esClient = (await context.core).elasticsearch.client; + const features = await getFeatures(); + + const { roles } = request.body; + const validatedRolesNames = []; + const kibanaErrors: RolesErrorsDetails = {}; + + for (const [roleName, role] of Object.entries(roles)) { + const { validationErrors } = validateKibanaPrivileges(features, role.kibana); + + if (validationErrors.length) { + kibanaErrors[roleName] = { + type: 'kibana_privilege_validation_exception', + reason: `Role cannot be updated due to validation errors: ${JSON.stringify( + validationErrors + )}`, + }; + + continue; + } + + validatedRolesNames.push(roleName); + } + + const rawRoles = await esClient.asCurrentUser.security.getRole( + { name: validatedRolesNames.join(',') }, + { ignore: [404] } + ); + + const esRolesPayload = Object.fromEntries( + validatedRolesNames.map((roleName) => [ + roleName, + transformPutPayloadToElasticsearchRole( + roles[roleName], + authz.applicationName, + rawRoles[roleName] ? rawRoles[roleName].applications : [] + ), + ]) + ); + + const esResponse = await esClient.asCurrentUser.transport.request<ESRolesResponse>({ + method: 'POST', + path: '/_security/role', + body: { roles: esRolesPayload }, + }); + + for (const roleName of [ + ...(esResponse.created ?? []), + ...(esResponse.updated ?? []), + ...(esResponse.noop ?? []), + ]) { + if (roleGrantsSubFeaturePrivileges(features, roles[roleName])) { + getFeatureUsageService().recordSubFeaturePrivilegeUsage(); + } + } + + const { created, noop, updated, errors: esErrors } = esResponse; + const hasAnyErrors = Object.keys(kibanaErrors).length || esErrors?.count; + + return response.ok({ + body: { + created, + noop, + updated, + ...(hasAnyErrors && { + errors: { ...kibanaErrors, ...(esErrors?.details ?? {}) }, + }), + }, + }); + } catch (error) { + return response.customError(wrapIntoCustomErrorResponse(error)); + } + }) + ); +} diff --git a/x-pack/plugins/security/server/routes/authorization/roles/put.ts b/x-pack/plugins/security/server/routes/authorization/roles/put.ts index 3a62b93819bd6..57271235add36 100644 --- a/x-pack/plugins/security/server/routes/authorization/roles/put.ts +++ b/x-pack/plugins/security/server/routes/authorization/roles/put.ts @@ -6,36 +6,14 @@ */ import { schema } from '@kbn/config-schema'; -import type { KibanaFeature } from '@kbn/features-plugin/common'; -import type { RolePayloadSchemaType } from './model'; +import { roleGrantsSubFeaturePrivileges } from './lib'; import { getPutPayloadSchema, transformPutPayloadToElasticsearchRole } from './model'; import type { RouteDefinitionParams } from '../..'; import { wrapIntoCustomErrorResponse } from '../../../errors'; import { validateKibanaPrivileges } from '../../../lib'; import { createLicensedRouteHandler } from '../../licensed_route_handler'; -const roleGrantsSubFeaturePrivileges = (features: KibanaFeature[], role: RolePayloadSchemaType) => { - if (!role.kibana) { - return false; - } - - const subFeaturePrivileges = new Map( - features.map((feature) => [ - feature.id, - feature.subFeatures.map((sf) => sf.privilegeGroups.map((pg) => pg.privileges)).flat(2), - ]) - ); - - const hasAnySubFeaturePrivileges = role.kibana.some((kibanaPrivilege) => - Object.entries(kibanaPrivilege.feature ?? {}).some(([featureId, privileges]) => { - return !!subFeaturePrivileges.get(featureId)?.some(({ id }) => privileges.includes(id)); - }) - ); - - return hasAnySubFeaturePrivileges; -}; - export function definePutRolesRoutes({ router, authz, diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.gen.ts new file mode 100644 index 0000000000000..5613a117aceb5 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Bootstrap Prebuilt Rules API endpoint + * version: 1 + */ + +import { z } from 'zod'; + +export type PackageInstallStatus = z.infer<typeof PackageInstallStatus>; +export const PackageInstallStatus = z.object({ + /** + * The name of the package + */ + name: z.string(), + /** + * The version of the package + */ + version: z.string(), + /** + * The status of the package installation + */ + status: z.enum(['installed', 'already_installed']), +}); + +export type BootstrapPrebuiltRulesResponse = z.infer<typeof BootstrapPrebuiltRulesResponse>; +export const BootstrapPrebuiltRulesResponse = z.object({ + /** + * The list of packages that were installed or upgraded + */ + packages: z.array(PackageInstallStatus), +}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.schema.yaml new file mode 100644 index 0000000000000..92cb6ccaf2ad1 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.schema.yaml @@ -0,0 +1,51 @@ +openapi: 3.0.0 +info: + title: Bootstrap Prebuilt Rules API endpoint + version: '1' +paths: + /internal/detection_engine/prebuilt_rules/_bootstrap: + post: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: BootstrapPrebuiltRules + summary: Bootstrap Prebuilt Rules + description: Ensures that the packages needed for prebuilt detection rules to work are installed and up to date + tags: + - Prebuilt Rules API + responses: + 200: + description: Indicates a successful call + content: + application/json: + schema: + type: object + properties: + packages: + type: array + description: The list of packages that were installed or upgraded + items: + $ref: '#/components/schemas/PackageInstallStatus' + required: + - packages + +components: + schemas: + PackageInstallStatus: + type: object + properties: + name: + type: string + description: The name of the package + version: + type: string + description: The version of the package + status: + type: string + description: The status of the package installation + enum: + - installed + - already_installed + required: + - name + - version + - status diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/urls.ts b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/urls.ts index ae62433d93a35..3f744dffc9447 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/urls.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/prebuilt_rules/urls.ts @@ -10,14 +10,15 @@ import { INTERNAL_DETECTION_ENGINE_URL as INTERNAL, } from '../../../constants'; -const OLD_BASE_URL = `${RULES}/prepackaged` as const; -const NEW_BASE_URL = `${INTERNAL}/prebuilt_rules` as const; +const LEGACY_BASE_URL = `${RULES}/prepackaged` as const; +const BASE_URL = `${INTERNAL}/prebuilt_rules` as const; -export const PREBUILT_RULES_URL = OLD_BASE_URL; -export const PREBUILT_RULES_STATUS_URL = `${OLD_BASE_URL}/_status` as const; +export const PREBUILT_RULES_URL = LEGACY_BASE_URL; +export const PREBUILT_RULES_STATUS_URL = `${LEGACY_BASE_URL}/_status` as const; -export const GET_PREBUILT_RULES_STATUS_URL = `${NEW_BASE_URL}/status` as const; -export const REVIEW_RULE_UPGRADE_URL = `${NEW_BASE_URL}/upgrade/_review` as const; -export const PERFORM_RULE_UPGRADE_URL = `${NEW_BASE_URL}/upgrade/_perform` as const; -export const REVIEW_RULE_INSTALLATION_URL = `${NEW_BASE_URL}/installation/_review` as const; -export const PERFORM_RULE_INSTALLATION_URL = `${NEW_BASE_URL}/installation/_perform` as const; +export const GET_PREBUILT_RULES_STATUS_URL = `${BASE_URL}/status` as const; +export const BOOTSTRAP_PREBUILT_RULES_URL = `${BASE_URL}/_bootstrap` as const; +export const REVIEW_RULE_UPGRADE_URL = `${BASE_URL}/upgrade/_review` as const; +export const PERFORM_RULE_UPGRADE_URL = `${BASE_URL}/upgrade/_perform` as const; +export const REVIEW_RULE_INSTALLATION_URL = `${BASE_URL}/installation/_review` as const; +export const PERFORM_RULE_INSTALLATION_URL = `${BASE_URL}/installation/_perform` as const; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/action_log.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/action_log.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/deprecated_action_log.gen.ts similarity index 59% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log.gen.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/deprecated_action_log.gen.ts index bfbea46ed9c5c..e458ae1775479 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/deprecated_action_log.gen.ts @@ -10,23 +10,18 @@ * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. * * info: - * title: Audit Log Schema + * title: Action Log Schema * version: 2023-10-31 */ import { z } from 'zod'; -import { Page, PageSize, StartDate, EndDate, AgentId } from '../model/schema/common.gen'; +import { Page, PageSize, StartDate, EndDate } from '../../model/schema/common.gen'; -export type AuditLogRequestQuery = z.infer<typeof AuditLogRequestQuery>; -export const AuditLogRequestQuery = z.object({ +export type ActionLogRequestQuery = z.infer<typeof ActionLogRequestQuery>; +export const ActionLogRequestQuery = z.object({ page: Page.optional(), page_size: PageSize.optional(), start_date: StartDate.optional(), end_date: EndDate.optional(), }); - -export type AuditLogRequestParams = z.infer<typeof AuditLogRequestParams>; -export const AuditLogRequestParams = z.object({ - agent_id: AgentId.optional(), -}); diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/deprecated_action_log.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/deprecated_action_log.schema.yaml new file mode 100644 index 0000000000000..d46a1cbe7d9ba --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/deprecated_action_log.schema.yaml @@ -0,0 +1,45 @@ +openapi: 3.0.0 +info: + title: Action Log Schema + version: '2023-10-31' +paths: + /api/endpoint/action_log/{agent_id}: + get: + summary: Get action requests log schema + operationId: EndpointGetActionLog + description: Get action requests log + deprecated: true + x-codegen-enabled: false + x-labels: [ess, serverless] + parameters: + - name: agent_id + in: path + required: true + schema: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/AgentId' + - name: query + in: query + required: true + schema: + $ref: '#/components/schemas/ActionLogRequestQuery' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + +components: + schemas: + ActionLogRequestQuery: + type: object + properties: + page: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/Page' + page_size: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/PageSize' + start_date: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/StartDate' + end_date: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/EndDate' diff --git a/x-pack/plugins/observability_solution/assets_data_access/server/types.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/index.ts similarity index 70% rename from x-pack/plugins/observability_solution/assets_data_access/server/types.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/index.ts index bfecff00b2729..61a6f0bcbd5c9 100644 --- a/x-pack/plugins/observability_solution/assets_data_access/server/types.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/action_log/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AssetsPluginStartDeps {} +export * from './action_log'; +export * from './deprecated_action_log.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/actions.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/actions.schema.yaml deleted file mode 100644 index a64eb5c5cff3a..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/actions.schema.yaml +++ /dev/null @@ -1,131 +0,0 @@ -openapi: 3.0.0 -info: - title: Endpoint Actions Schema - version: '2023-10-31' -paths: - /api/endpoint/action/state: - get: - summary: Get Action State schema - operationId: EndpointGetActionsState - x-codegen-enabled: false - x-labels: - - ess - - serverless - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - - /api/endpoint/action/running_procs: - post: - summary: Get Running Processes Action - operationId: EndpointGetRunningProcessesAction - x-codegen-enabled: false - x-labels: - - ess - - serverless - requestBody: - required: true - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - - /api/endpoint/action/isolate: - post: - summary: Isolate host Action - operationId: EndpointIsolateHostAction - x-codegen-enabled: false - x-labels: - - ess - - serverless - requestBody: - required: true - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - - /api/endpoint/action/unisolate: - post: - summary: Unisolate host Action - operationId: EndpointUnisolateHostAction - x-codegen-enabled: false - x-labels: - - ess - - serverless - requestBody: - required: true - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - - /api/endpoint/action/kill_process: - post: - summary: Kill process Action - operationId: EndpointKillProcessAction - x-codegen-enabled: false - x-labels: - - ess - - serverless - requestBody: - required: true - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/ProcessActionSchemas' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - - - /api/endpoint/action/suspend_process: - post: - summary: Suspend process Action - operationId: EndpointSuspendProcessAction - x-codegen-enabled: false - x-labels: - - ess - - serverless - requestBody: - required: true - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/ProcessActionSchemas' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log.schema.yaml deleted file mode 100644 index 5d0ed51ca339a..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/audit_log.schema.yaml +++ /dev/null @@ -1,51 +0,0 @@ -openapi: 3.0.0 -info: - title: Audit Log Schema - version: '2023-10-31' -paths: - /api/endpoint/action_log/{agent_id}: - get: - summary: Get action audit log schema - operationId: EndpointGetActionAuditLog - x-codegen-enabled: false - x-labels: - - ess - - serverless - parameters: - - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/AuditLogRequestQuery' - - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/AuditLogRequestParams' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - -components: - schemas: - AuditLogRequestQuery: - type: object - properties: - page: - $ref: '../model/schema/common.schema.yaml#/components/schemas/Page' - page_size: - $ref: '../model/schema/common.schema.yaml#/components/schemas/PageSize' - start_date: - $ref: '../model/schema/common.schema.yaml#/components/schemas/StartDate' - end_date: - $ref: '../model/schema/common.schema.yaml#/components/schemas/EndDate' - - AuditLogRequestParams: - type: object - properties: - agent_id: - $ref: '../model/schema/common.schema.yaml#/components/schemas/AgentId' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts index ca6d9d5e91982..56b1fafdb5a71 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/common/response_actions.ts @@ -7,24 +7,27 @@ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { - KillProcessRouteRequestSchema, - SuspendProcessRouteRequestSchema, - UploadActionRequestSchema, -} from '../..'; -import { ExecuteActionRequestSchema } from '../execute_route'; -import { EndpointActionGetFileSchema } from '../get_file_route'; -import { ScanActionRequestSchema } from '../scan_route'; -import { NoParametersRequestSchema } from './base'; + +import { ExecuteActionRequestSchema } from '../response_actions/execute'; +import { EndpointActionGetFileSchema } from '../response_actions/get_file'; +import { ScanActionRequestSchema } from '../response_actions/scan'; +import { IsolateRouteRequestSchema } from '../response_actions/isolate'; +import { UnisolateRouteRequestSchema } from '../response_actions/unisolate'; +import { GetProcessesRouteRequestSchema } from '../response_actions/running_procs'; +import { KillProcessRouteRequestSchema } from '../response_actions/kill_process'; +import { SuspendProcessRouteRequestSchema } from '../response_actions/suspend_process'; +import { UploadActionRequestSchema } from '../response_actions/upload'; export const ResponseActionBodySchema = schema.oneOf([ - NoParametersRequestSchema.body, + IsolateRouteRequestSchema.body, + UnisolateRouteRequestSchema.body, + GetProcessesRouteRequestSchema.body, KillProcessRouteRequestSchema.body, SuspendProcessRouteRequestSchema.body, EndpointActionGetFileSchema.body, ExecuteActionRequestSchema.body, - ScanActionRequestSchema.body, UploadActionRequestSchema.body, + ScanActionRequestSchema.body, ]); export type ResponseActionsRequestBody = TypeOf<typeof ResponseActionBodySchema>; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/details.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/details.gen.ts deleted file mode 100644 index dcceb64d44a6b..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/details.gen.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Details Schema - * version: 2023-10-31 - */ - -import { z } from 'zod'; - -export type DetailsRequestParams = z.infer<typeof DetailsRequestParams>; -export const DetailsRequestParams = z.object({ - action_id: z.string().optional(), -}); diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.gen.ts new file mode 100644 index 0000000000000..3f5305dabd424 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Details Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { SuccessResponse } from '../../model/schema/common.gen'; + +export type EndpointGetActionsDetailsRequestParams = z.infer< + typeof EndpointGetActionsDetailsRequestParams +>; +export const EndpointGetActionsDetailsRequestParams = z.object({ + action_id: z.string(), +}); +export type EndpointGetActionsDetailsRequestParamsInput = z.input< + typeof EndpointGetActionsDetailsRequestParams +>; + +export type EndpointGetActionsDetailsResponse = z.infer<typeof EndpointGetActionsDetailsResponse>; +export const EndpointGetActionsDetailsResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/details.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.schema.yaml similarity index 51% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/details.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.schema.yaml index 18a3ffa2c1fd2..ec3a184e6e9a9 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/details.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.schema.yaml @@ -7,28 +7,21 @@ paths: get: summary: Get Action details schema operationId: EndpointGetActionsDetails - x-codegen-enabled: false - x-labels: - - ess - - serverless + description: Get action details + x-codegen-enabled: true + x-labels: [ess, serverless] parameters: - - name: query + - name: action_id in: path required: true schema: - $ref: '#/components/schemas/DetailsRequestParams' + type: string responses: '200': description: OK content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' -components: - schemas: - DetailsRequestParams: - type: object - properties: - action_id: - type: string + $ref: '../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/details_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/details_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/details/details.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/details/index.ts similarity index 66% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/details/index.ts index 2d9f2d2f5725b..0154d63be42a2 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/details/index.ts @@ -5,6 +5,5 @@ * 2.0. */ -import { NoParametersRequestSchema } from './common/base'; - -export const UnisolateRouteRequestSchema = NoParametersRequestSchema; +export * from './details'; +export * from './details.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts deleted file mode 100644 index 8afd62814dfb3..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.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. - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Execute Action Schema - * version: 2023-10-31 - */ - -import { z } from 'zod'; - -import { BaseActionSchema, Command, Timeout } from '../model/schema/common.gen'; - -export type ExecuteActionRequestBody = z.infer<typeof ExecuteActionRequestBody>; -export const ExecuteActionRequestBody = BaseActionSchema.merge( - z.object({ - parameters: z.object({ - command: Command, - timeout: Timeout.optional(), - }), - }) -); diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download.schema.yaml deleted file mode 100644 index 8eb02883965b1..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download.schema.yaml +++ /dev/null @@ -1,39 +0,0 @@ -openapi: 3.0.0 -info: - title: File Download Schema - version: '2023-10-31' -paths: - /api/endpoint/action/{action_id}/file/{file_id}/download`: - get: - summary: File Download schema - operationId: EndpointFileDownload - x-codegen-enabled: false - x-labels: - - ess - - serverless - parameters: - - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/FileDownloadRequestParams' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' -components: - schemas: - FileDownloadRequestParams: - type: object - required: - - action_id - - file_id - properties: - action_id: - type: string - file_id: - type: string - diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.gen.ts new file mode 100644 index 0000000000000..a32c1036e3fd9 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: File Download Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { SuccessResponse } from '../../model/schema/common.gen'; + +export type EndpointFileDownloadRequestParams = z.infer<typeof EndpointFileDownloadRequestParams>; +export const EndpointFileDownloadRequestParams = z.object({ + action_id: z.string(), + file_id: z.string(), +}); +export type EndpointFileDownloadRequestParamsInput = z.input< + typeof EndpointFileDownloadRequestParams +>; + +export type EndpointFileDownloadResponse = z.infer<typeof EndpointFileDownloadResponse>; +export const EndpointFileDownloadResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.schema.yaml new file mode 100644 index 0000000000000..8842d1b6acc61 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.schema.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + title: File Download Schema + version: '2023-10-31' +paths: + /api/endpoint/action/{action_id}/file/{file_id}/download`: + get: + summary: File Download schema + operationId: EndpointFileDownload + description: Download a file from an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] + parameters: + - name: action_id + in: path + required: true + schema: + type: string + - name: file_id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/file_download_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/file_download.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/index.ts new file mode 100644 index 0000000000000..f6b87f11df80e --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download/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 './file_download'; +export * from './file_download.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info.schema.yaml deleted file mode 100644 index 5351480738e23..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info.schema.yaml +++ /dev/null @@ -1,40 +0,0 @@ -openapi: 3.0.0 -info: - title: File Info Schema - version: '2023-10-31' -paths: - /api/endpoint/action/{action_id}/file/{file_id}`: - get: - summary: File Info schema - operationId: EndpointFileInfo - x-codegen-enabled: false - x-labels: - - ess - - serverless - parameters: - - name: query - in: path - required: true - schema: - $ref: '#/components/schemas/FileInfoRequestParams' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - -components: - schemas: - FileInfoRequestParams: - type: object - required: - - action_id - - file_id - properties: - action_id: - type: string - file_id: - type: string - diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.gen.ts similarity index 54% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/file_info.gen.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.gen.ts index d9737560e849c..1a706049b067a 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.gen.ts @@ -16,8 +16,14 @@ import { z } from 'zod'; -export type FileInfoRequestParams = z.infer<typeof FileInfoRequestParams>; -export const FileInfoRequestParams = z.object({ +import { SuccessResponse } from '../../model/schema/common.gen'; + +export type EndpointFileInfoRequestParams = z.infer<typeof EndpointFileInfoRequestParams>; +export const EndpointFileInfoRequestParams = z.object({ action_id: z.string(), file_id: z.string(), }); +export type EndpointFileInfoRequestParamsInput = z.input<typeof EndpointFileInfoRequestParams>; + +export type EndpointFileInfoResponse = z.infer<typeof EndpointFileInfoResponse>; +export const EndpointFileInfoResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.schema.yaml new file mode 100644 index 0000000000000..6199dc56ed1b0 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.schema.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + title: File Info Schema + version: '2023-10-31' +paths: + /api/endpoint/action/{action_id}/file/{file_id}`: + get: + summary: File Info schema + operationId: EndpointFileInfo + description: Get file info + x-codegen-enabled: true + x-labels: [ess, serverless] + parameters: + - name: action_id + in: path + required: true + schema: + type: string + - name: file_id + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/file_info_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/file_info.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/index.ts new file mode 100644 index 0000000000000..db1f6c9ef2db3 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_info/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 './file_info'; +export * from './file_info.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts deleted file mode 100644 index f376c6d81fc21..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.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. - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: File Upload Schema - * version: 2023-10-31 - */ - -import { z } from 'zod'; - -import { BaseActionSchema } from '../model/schema/common.gen'; - -export type FileUploadActionRequestBody = z.infer<typeof FileUploadActionRequestBody>; -export const FileUploadActionRequestBody = BaseActionSchema.merge( - z.object({ - parameters: z.object({ - overwrite: z.boolean().optional().default(false), - }), - file: z.string(), - }) -); diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts deleted file mode 100644 index 22dc90c6cfd82..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Get File Schema - * version: 2023-10-31 - */ - -import { z } from 'zod'; - -import { BaseActionSchema } from '../model/schema/common.gen'; - -export type GetFileActionRequestBody = z.infer<typeof GetFileActionRequestBody>; -export const GetFileActionRequestBody = BaseActionSchema.merge( - z.object({ - parameters: z.object({ - path: z.string(), - }), - }) -); diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/list.schema.yaml deleted file mode 100644 index afa844135ecd5..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.schema.yaml +++ /dev/null @@ -1,53 +0,0 @@ -openapi: 3.0.0 -info: - title: Actions List Schema - version: '2023-10-31' -paths: - /api/endpoint/action: - get: - summary: Get Actions List schema - operationId: EndpointGetActionsList - x-codegen-enabled: false - x-labels: - - ess - - serverless - parameters: - - name: query - in: query - required: true - schema: - $ref: '#/components/schemas/EndpointActionListRequestQuery' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' -components: - schemas: - EndpointActionListRequestQuery: - type: object - properties: - agentIds: - $ref: '../model/schema/common.schema.yaml#/components/schemas/AgentIds' - commands: - $ref: '../model/schema/common.schema.yaml#/components/schemas/Commands' - page: - $ref: '../model/schema/common.schema.yaml#/components/schemas/Page' - pageSize: - type: integer - default: 10 - minimum: 1 - maximum: 10000 - description: Number of items per page - startDate: - $ref: '../model/schema/common.schema.yaml#/components/schemas/StartDate' - endDate: - $ref: '../model/schema/common.schema.yaml#/components/schemas/EndDate' - userIds: - $ref: '../model/schema/common.schema.yaml#/components/schemas/UserIds' - types: - $ref: '../model/schema/common.schema.yaml#/components/schemas/Types' - withOutputs: - $ref: '../model/schema/common.schema.yaml#/components/schemas/WithOutputs' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/list/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/index.ts new file mode 100644 index 0000000000000..9b1e437559465 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/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 './list'; +export * from './list.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.gen.ts similarity index 56% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/list.gen.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.gen.ts index 771af7165f3bd..a63e011e1d2f5 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/list.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.gen.ts @@ -17,7 +17,9 @@ import { z } from 'zod'; import { + SuccessResponse, AgentIds, + AgentTypes, Commands, Page, StartDate, @@ -25,11 +27,12 @@ import { UserIds, Types, WithOutputs, -} from '../model/schema/common.gen'; +} from '../../model/schema/common.gen'; -export type EndpointActionListRequestQuery = z.infer<typeof EndpointActionListRequestQuery>; -export const EndpointActionListRequestQuery = z.object({ +export type GetEndpointActionListRouteQuery = z.infer<typeof GetEndpointActionListRouteQuery>; +export const GetEndpointActionListRouteQuery = z.object({ agentIds: AgentIds.optional(), + agentTypes: AgentTypes.optional(), commands: Commands.optional(), page: Page.optional(), /** @@ -42,3 +45,14 @@ export const EndpointActionListRequestQuery = z.object({ types: Types.optional(), withOutputs: WithOutputs.optional(), }); + +export type EndpointGetActionsListRequestQuery = z.infer<typeof EndpointGetActionsListRequestQuery>; +export const EndpointGetActionsListRequestQuery = z.object({ + query: GetEndpointActionListRouteQuery, +}); +export type EndpointGetActionsListRequestQueryInput = z.input< + typeof EndpointGetActionsListRequestQuery +>; + +export type EndpointGetActionsListResponse = z.infer<typeof EndpointGetActionsListResponse>; +export const EndpointGetActionsListResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.schema.yaml new file mode 100644 index 0000000000000..b91ba03c60b8d --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.schema.yaml @@ -0,0 +1,54 @@ +openapi: 3.0.0 +info: + title: Actions List Schema + version: '2023-10-31' +paths: + /api/endpoint/action: + get: + summary: Get Actions List schema + operationId: EndpointGetActionsList + description: Get a list of action requests and their responses + x-codegen-enabled: true + x-labels: [ess, serverless] + parameters: + - name: query + in: query + required: true + schema: + $ref: '#/components/schemas/GetEndpointActionListRouteQuery' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' +components: + schemas: + GetEndpointActionListRouteQuery: + type: object + properties: + agentIds: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/AgentIds' + agentTypes: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/AgentTypes' + commands: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/Commands' + page: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/Page' + pageSize: + type: integer + default: 10 + minimum: 1 + maximum: 10000 + description: Number of items per page + startDate: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/StartDate' + endDate: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/EndDate' + userIds: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/UserIds' + types: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/Types' + withOutputs: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/WithOutputs' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/list_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.ts similarity index 94% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/list_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.ts index 720764d9cd03c..81cd32a045a7d 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/list_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/list/list.ts @@ -12,9 +12,9 @@ import { RESPONSE_ACTION_API_COMMANDS_NAMES, RESPONSE_ACTION_STATUS, RESPONSE_ACTION_TYPE, -} from '../../../endpoint/service/response_actions/constants'; -import { ENDPOINT_DEFAULT_PAGE_SIZE } from '../../../endpoint/constants'; -import { agentTypesSchema } from './common/base'; +} from '../../../../endpoint/service/response_actions/constants'; +import { ENDPOINT_DEFAULT_PAGE_SIZE } from '../../../../endpoint/constants'; +import { agentTypesSchema } from '../common/base'; const commandsSchema = schema.oneOf( // @ts-expect-error TS2769: No overload matches this call diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.gen.ts new file mode 100644 index 0000000000000..29c30de059fc7 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Execute Action Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { + SuccessResponse, + BaseActionSchema, + Command, + Timeout, +} from '../../../model/schema/common.gen'; + +export type ExecuteRouteRequestBody = z.infer<typeof ExecuteRouteRequestBody>; +export const ExecuteRouteRequestBody = BaseActionSchema.merge( + z.object({ + parameters: z.object({ + command: Command, + timeout: Timeout.optional(), + }), + }) +); + +export type EndpointExecuteActionRequestBody = z.infer<typeof EndpointExecuteActionRequestBody>; +export const EndpointExecuteActionRequestBody = ExecuteRouteRequestBody; +export type EndpointExecuteActionRequestBodyInput = z.input< + typeof EndpointExecuteActionRequestBody +>; + +export type EndpointExecuteActionResponse = z.infer<typeof EndpointExecuteActionResponse>; +export const EndpointExecuteActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.schema.yaml similarity index 54% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/execute.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.schema.yaml index ea9e7d7d98bf9..beafae76a4ba6 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.schema.yaml @@ -7,29 +7,28 @@ paths: post: summary: Execute Action operationId: EndpointExecuteAction - x-codegen-enabled: false - x-labels: - - ess - - serverless + description: Execute a given command on an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ExecuteActionRequestBody' + $ref: '#/components/schemas/ExecuteRouteRequestBody' responses: '200': description: OK content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' components: schemas: - ExecuteActionRequestBody: + ExecuteRouteRequestBody: allOf: - - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' + - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' - type: object required: - parameters @@ -40,6 +39,6 @@ components: type: object properties: command: - $ref: '../model/schema/common.schema.yaml#/components/schemas/Command' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/Command' timeout: - $ref: '../model/schema/common.schema.yaml#/components/schemas/Timeout' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/Timeout' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.ts similarity index 94% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/execute_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.ts index b8de2fc6874a0..f7f4394d66089 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/execute.ts @@ -7,7 +7,7 @@ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { BaseActionRequestSchema } from './common/base'; +import { BaseActionRequestSchema } from '../../common/base'; export const ExecuteActionRequestSchema = { body: schema.object({ diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/index.ts new file mode 100644 index 0000000000000..be6732a4be40d --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/execute/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 './execute'; +export * from './execute.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.gen.ts new file mode 100644 index 0000000000000..e5c91527f5431 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Get File Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { SuccessResponse, BaseActionSchema } from '../../../model/schema/common.gen'; + +export type GetFileRouteRequestBody = z.infer<typeof GetFileRouteRequestBody>; +export const GetFileRouteRequestBody = BaseActionSchema.merge( + z.object({ + parameters: z.object({ + path: z.string(), + }), + }) +); + +export type EndpointGetFileActionRequestBody = z.infer<typeof EndpointGetFileActionRequestBody>; +export const EndpointGetFileActionRequestBody = GetFileRouteRequestBody; +export type EndpointGetFileActionRequestBodyInput = z.input< + typeof EndpointGetFileActionRequestBody +>; + +export type EndpointGetFileActionResponse = z.infer<typeof EndpointGetFileActionResponse>; +export const EndpointGetFileActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.schema.yaml similarity index 64% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.schema.yaml index d79c07556da82..a5211580d7e42 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.schema.yaml @@ -1,3 +1,4 @@ + openapi: 3.0.0 info: title: Get File Schema @@ -7,29 +8,28 @@ paths: post: summary: Get File Action operationId: EndpointGetFileAction - x-codegen-enabled: false - x-labels: - - ess - - serverless + description: Get a file from an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/GetFileActionRequestBody' + $ref: '#/components/schemas/GetFileRouteRequestBody' responses: '200': description: OK content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' components: schemas: - GetFileActionRequestBody: + GetFileRouteRequestBody: allOf: - - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' + - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' - type: object required: - parameters diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.ts similarity index 91% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/get_file_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.ts index 4378042f29403..0eb11cf538be5 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/get_file.ts @@ -7,7 +7,7 @@ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { BaseActionRequestSchema } from './common/base'; +import { BaseActionRequestSchema } from '../../common/base'; export const EndpointActionGetFileSchema = { body: schema.object({ diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/index.ts new file mode 100644 index 0000000000000..b061771b5c3c2 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/get_file/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 './get_file'; +export * from './get_file.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/deprecated_isolate.gen.ts similarity index 91% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.gen.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/deprecated_isolate.gen.ts index 1318e25d827f8..4b179d47bea2a 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/deprecated_isolate.gen.ts @@ -16,7 +16,7 @@ import type { z } from 'zod'; -import { BaseActionSchema, SuccessResponse } from '../model/schema/common.gen'; +import { BaseActionSchema, SuccessResponse } from '../../../model/schema/common.gen'; export type EndpointIsolateRedirectRequestBody = z.infer<typeof EndpointIsolateRedirectRequestBody>; export const EndpointIsolateRedirectRequestBody = BaseActionSchema; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/deprecated_isolate.schema.yaml similarity index 75% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/deprecated_isolate.schema.yaml index 4342dd85c7080..89d97c948c5d9 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/deprecated_isolate.schema.yaml @@ -7,15 +7,15 @@ paths: post: summary: Permanently redirects to a new location operationId: EndpointIsolateRedirect + deprecated: true x-codegen-enabled: true - x-labels: - - ess + x-labels: [ess] requestBody: required: true content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' responses: '308': description: Permanent Redirect @@ -30,4 +30,4 @@ paths: content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/index.ts new file mode 100644 index 0000000000000..b4903efed2144 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/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 './isolate'; +export * from './isolate.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.ts new file mode 100644 index 0000000000000..d3e9ee2f2588a --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Isolate Schema + * version: 2023-10-31 + */ + +import type { z } from 'zod'; + +import { SuccessResponse, NoParametersRequestSchema } from '../../../model/schema/common.gen'; + +export type IsolateRouteRequestBody = z.infer<typeof IsolateRouteRequestBody>; +export const IsolateRouteRequestBody = NoParametersRequestSchema; + +export type EndpointIsolateActionRequestBody = z.infer<typeof EndpointIsolateActionRequestBody>; +export const EndpointIsolateActionRequestBody = IsolateRouteRequestBody; +export type EndpointIsolateActionRequestBodyInput = z.input< + typeof EndpointIsolateActionRequestBody +>; + +export type EndpointIsolateActionResponse = z.infer<typeof EndpointIsolateActionResponse>; +export const EndpointIsolateActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.schema.yaml new file mode 100644 index 0000000000000..f721c69efa570 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.schema.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + title: Isolate Schema + version: '2023-10-31' +paths: + /api/endpoint/action/isolate: + post: + summary: Isolate Action + operationId: EndpointIsolateAction + description: Isolate an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/IsolateRouteRequestBody' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + +components: + schemas: + IsolateRouteRequestBody: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/NoParametersRequestSchema' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.ts similarity index 87% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.ts index 0df0d8d913457..787b2cb4362ec 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/isolate_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.ts @@ -6,7 +6,7 @@ */ import type { TypeOf } from '@kbn/config-schema'; -import { NoParametersRequestSchema } from './common/base'; +import { NoParametersRequestSchema } from '../../common/base'; export const IsolateRouteRequestSchema = NoParametersRequestSchema; export type IsolationRouteRequestBody = TypeOf<typeof IsolateRouteRequestSchema.body>; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/index.ts new file mode 100644 index 0000000000000..cd316877f542d --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/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 './kill_process'; +export * from './kill_process.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.gen.ts new file mode 100644 index 0000000000000..dc24dacf3e120 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Kill Process Schema + * version: 2023-10-31 + */ + +import type { z } from 'zod'; + +import { KillOrSuspendActionSchema, SuccessResponse } from '../../../model/schema/common.gen'; + +export type EndpointKillProcessActionRequestBody = z.infer< + typeof EndpointKillProcessActionRequestBody +>; +export const EndpointKillProcessActionRequestBody = KillOrSuspendActionSchema; +export type EndpointKillProcessActionRequestBodyInput = z.input< + typeof EndpointKillProcessActionRequestBody +>; + +export type EndpointKillProcessActionResponse = z.infer<typeof EndpointKillProcessActionResponse>; +export const EndpointKillProcessActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.schema.yaml new file mode 100644 index 0000000000000..0014026664fe2 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.schema.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + title: Kill Process Schema + version: '2023-10-31' +paths: + /api/endpoint/action/kill_process: + post: + summary: Kill process Action + operationId: EndpointKillProcessAction + description: Kill a running process on an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] + requestBody: + required: true + content: + application/json: + schema: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/KillOrSuspendActionSchema' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/kill_process_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.ts similarity index 88% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/kill_process_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.ts index f3c0d4e8f12be..0d42cb8badda1 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/kill_process_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/kill_process/kill_process.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { BaseActionRequestSchema } from './common/base'; +import { BaseActionRequestSchema } from '../../common/base'; // -------------------------------------------------- // Tests for this module are at: @@ -41,3 +42,5 @@ export const KillProcessRouteRequestSchema = { } ), }; + +export type KillProcessRequestBody = TypeOf<typeof KillProcessRouteRequestSchema.body>; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/index.ts new file mode 100644 index 0000000000000..d44f9455c9807 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/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 './running_procs'; +export * from './running_procs.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.ts new file mode 100644 index 0000000000000..8050f78c1f72c --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Get Running Processes Schema + * version: 2023-10-31 + */ + +import type { z } from 'zod'; + +import { SuccessResponse, NoParametersRequestSchema } from '../../../model/schema/common.gen'; + +export type GetProcessesRouteRequestBody = z.infer<typeof GetProcessesRouteRequestBody>; +export const GetProcessesRouteRequestBody = NoParametersRequestSchema; + +export type EndpointGetProcessesActionRequestBody = z.infer< + typeof EndpointGetProcessesActionRequestBody +>; +export const EndpointGetProcessesActionRequestBody = GetProcessesRouteRequestBody; +export type EndpointGetProcessesActionRequestBodyInput = z.input< + typeof EndpointGetProcessesActionRequestBody +>; + +export type EndpointGetProcessesActionResponse = z.infer<typeof EndpointGetProcessesActionResponse>; +export const EndpointGetProcessesActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.schema.yaml new file mode 100644 index 0000000000000..0d5ced3b205f4 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.schema.yaml @@ -0,0 +1,31 @@ +openapi: 3.0.0 +info: + title: Get Running Processes Schema + version: '2023-10-31' +paths: + /api/endpoint/action/running_procs: + post: + summary: Get Running Processes Action + operationId: EndpointGetProcessesAction + description: Get list of running processes on an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/GetProcessesRouteRequestBody' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + +components: + schemas: + GetProcessesRouteRequestBody: + allOf: + - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/NoParametersRequestSchema' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_processes_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.ts similarity index 88% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/get_processes_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.ts index a9e56e52ba292..e9caec65e2aa3 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_processes_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.ts @@ -6,7 +6,7 @@ */ import type { TypeOf } from '@kbn/config-schema'; -import { NoParametersRequestSchema } from './common/base'; +import { NoParametersRequestSchema } from '../../common/base'; export const GetProcessesRouteRequestSchema = NoParametersRequestSchema; export type GetProcessesRequestBody = TypeOf<typeof GetProcessesRouteRequestSchema.body>; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/index.ts new file mode 100644 index 0000000000000..b7170f14f6d48 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/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 './scan'; +export * from './scan.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.gen.ts new file mode 100644 index 0000000000000..302f5de95eaa0 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Scan Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { SuccessResponse, BaseActionSchema } from '../../../model/schema/common.gen'; + +export type ScanRouteRequestBody = z.infer<typeof ScanRouteRequestBody>; +export const ScanRouteRequestBody = BaseActionSchema.merge( + z.object({ + parameters: z.object({ + path: z.string(), + }), + }) +); + +export type EndpointScanActionRequestBody = z.infer<typeof EndpointScanActionRequestBody>; +export const EndpointScanActionRequestBody = ScanRouteRequestBody; +export type EndpointScanActionRequestBodyInput = z.input<typeof EndpointScanActionRequestBody>; + +export type EndpointScanActionResponse = z.infer<typeof EndpointScanActionResponse>; +export const EndpointScanActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/scan.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.schema.yaml similarity index 64% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/scan.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.schema.yaml index ee7e9c1a9a4a7..beea986e4546c 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/scan.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.schema.yaml @@ -7,29 +7,28 @@ paths: post: summary: Scan Action operationId: EndpointScanAction - x-codegen-enabled: false - x-labels: - - ess - - serverless + description: Scan a file or directory + x-codegen-enabled: true + x-labels: [ess, serverless] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/ScanActionRequestBody' + $ref: '#/components/schemas/ScanRouteRequestBody' responses: '200': description: OK content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' components: schemas: - ScanActionRequestBody: + ScanRouteRequestBody: allOf: - - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' + - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' - type: object required: - parameters diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/scan_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.ts similarity index 92% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/scan_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.ts index 1c9b3c6980d90..3cbc04fdf4555 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/scan_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/scan/scan.ts @@ -7,7 +7,7 @@ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { BaseActionRequestSchema } from './common/base'; +import { BaseActionRequestSchema } from '../../common/base'; export const ScanActionRequestSchema = { body: schema.object({ diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/index.ts new file mode 100644 index 0000000000000..ff4d08a07f23f --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/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 './suspend_process'; +export * from './suspend_process.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.gen.ts new file mode 100644 index 0000000000000..6c5fb52525628 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Suspend Process Schema + * version: 2023-10-31 + */ + +import type { z } from 'zod'; + +import { KillOrSuspendActionSchema, SuccessResponse } from '../../../model/schema/common.gen'; + +export type EndpointSuspendProcessActionRequestBody = z.infer< + typeof EndpointSuspendProcessActionRequestBody +>; +export const EndpointSuspendProcessActionRequestBody = KillOrSuspendActionSchema; +export type EndpointSuspendProcessActionRequestBodyInput = z.input< + typeof EndpointSuspendProcessActionRequestBody +>; + +export type EndpointSuspendProcessActionResponse = z.infer< + typeof EndpointSuspendProcessActionResponse +>; +export const EndpointSuspendProcessActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.schema.yaml new file mode 100644 index 0000000000000..f5f5b1e46ed2d --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.schema.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + title: Suspend Process Schema + version: '2023-10-31' +paths: + /api/endpoint/action/suspend_process: + post: + summary: Suspend process Action + operationId: EndpointSuspendProcessAction + description: Suspend a running process on an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] + requestBody: + required: true + content: + application/json: + schema: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/KillOrSuspendActionSchema' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/suspend_process_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.ts similarity index 73% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/suspend_process_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.ts index 81edb01197c69..d7cbbdc2b21e6 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/suspend_process_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { BaseActionRequestSchema } from './common/base'; +import { BaseActionRequestSchema } from '../../common/base'; export const SuspendProcessRouteRequestSchema = { body: schema.object({ @@ -17,3 +18,5 @@ export const SuspendProcessRouteRequestSchema = { ]), }), }; + +export type SuspendProcessRequestBody = TypeOf<typeof SuspendProcessRouteRequestSchema.body>; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/deprecated_unisolate.gen.ts similarity index 91% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.gen.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/deprecated_unisolate.gen.ts index d4c68a439c172..42ed374c24fce 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/deprecated_unisolate.gen.ts @@ -16,7 +16,7 @@ import type { z } from 'zod'; -import { BaseActionSchema, SuccessResponse } from '../model/schema/common.gen'; +import { BaseActionSchema, SuccessResponse } from '../../../model/schema/common.gen'; export type EndpointUnisolateRedirectRequestBody = z.infer< typeof EndpointUnisolateRedirectRequestBody diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/deprecated_unisolate.schema.yaml similarity index 76% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/deprecated_unisolate.schema.yaml index 570854a054799..1d347f90fed44 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/unisolate_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/deprecated_unisolate.schema.yaml @@ -7,15 +7,15 @@ paths: post: summary: Permanently redirects to a new location operationId: EndpointUnisolateRedirect + deprecated: true x-codegen-enabled: true - x-labels: - - ess + x-labels: [ess] requestBody: required: true content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' responses: '308': description: Permanent Redirect @@ -30,4 +30,4 @@ paths: content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/index.ts new file mode 100644 index 0000000000000..46e542a8d1ef1 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './unisolate'; +export * from './unisolate.gen'; +export * from './deprecated_unisolate.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.ts new file mode 100644 index 0000000000000..bf0fd2da1a605 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Unisolate Schema + * version: 2023-10-31 + */ + +import type { z } from 'zod'; + +import { SuccessResponse, NoParametersRequestSchema } from '../../../model/schema/common.gen'; + +export type UnisolateRouteRequestBody = z.infer<typeof UnisolateRouteRequestBody>; +export const UnisolateRouteRequestBody = NoParametersRequestSchema; + +export type EndpointUnisolateActionRequestBody = z.infer<typeof EndpointUnisolateActionRequestBody>; +export const EndpointUnisolateActionRequestBody = UnisolateRouteRequestBody; +export type EndpointUnisolateActionRequestBodyInput = z.input< + typeof EndpointUnisolateActionRequestBody +>; + +export type EndpointUnisolateActionResponse = z.infer<typeof EndpointUnisolateActionResponse>; +export const EndpointUnisolateActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.schema.yaml new file mode 100644 index 0000000000000..6c12a21f3241a --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.schema.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.0 +info: + title: Unisolate Schema + version: '2023-10-31' +paths: + /api/endpoint/action/unisolate: + post: + summary: Unisolate Action + operationId: EndpointUnisolateAction + description: Release an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UnisolateRouteRequestBody' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + +components: + schemas: + UnisolateRouteRequestBody: + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/NoParametersRequestSchema' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.ts new file mode 100644 index 0000000000000..84d63886bb8fa --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { TypeOf } from '@kbn/config-schema'; +import { NoParametersRequestSchema } from '../../common/base'; + +export const UnisolateRouteRequestSchema = NoParametersRequestSchema; +export type UnisolationRouteRequestBody = TypeOf<typeof UnisolateRouteRequestSchema.body>; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/index.ts new file mode 100644 index 0000000000000..34071a503e98f --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/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 './upload'; +export * from './upload.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.gen.ts new file mode 100644 index 0000000000000..81a908d0b8728 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: File Upload Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { SuccessResponse, BaseActionSchema } from '../../../model/schema/common.gen'; + +export type UploadRouteRequestBody = z.infer<typeof UploadRouteRequestBody>; +export const UploadRouteRequestBody = BaseActionSchema.merge( + z.object({ + parameters: z.object({ + overwrite: z.boolean().optional().default(false), + }), + file: z.string(), + }) +); + +export type EndpointUploadActionRequestBody = z.infer<typeof EndpointUploadActionRequestBody>; +export const EndpointUploadActionRequestBody = UploadRouteRequestBody; +export type EndpointUploadActionRequestBodyInput = z.input<typeof EndpointUploadActionRequestBody>; + +export type EndpointUploadActionResponse = z.infer<typeof EndpointUploadActionResponse>; +export const EndpointUploadActionResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.schema.yaml similarity index 68% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.schema.yaml index fa9f0da1b1203..ff62065ae5403 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.schema.yaml @@ -7,29 +7,28 @@ paths: post: summary: Upload Action operationId: EndpointUploadAction - x-codegen-enabled: false - x-labels: - - ess - - serverless + description: Upload a file to an endpoint + x-codegen-enabled: true + x-labels: [ess, serverless] requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/FileUploadActionRequestBody' + $ref: '#/components/schemas/UploadRouteRequestBody' responses: '200': description: OK content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' components: schemas: - FileUploadActionRequestBody: + UploadRouteRequestBody: allOf: - - $ref: '../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' + - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' - type: object required: - parameters diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.ts similarity index 94% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.ts index b5850a63ca8e0..5bebdfa58c0b0 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload_route.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/response_actions/upload/upload.ts @@ -7,7 +7,7 @@ import type { TypeOf } from '@kbn/config-schema'; import { schema } from '@kbn/config-schema'; -import { BaseActionRequestSchema } from './common/base'; +import { BaseActionRequestSchema } from '../../common/base'; export const UploadActionRequestSchema = { body: schema.object({ diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/scan.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/scan.gen.ts deleted file mode 100644 index 0aa58c33b8ebf..0000000000000 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/scan.gen.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Scan Schema - * version: 2023-10-31 - */ - -import { z } from 'zod'; - -import { BaseActionSchema } from '../model/schema/common.gen'; - -export type ScanActionRequestBody = z.infer<typeof ScanActionRequestBody>; -export const ScanActionRequestBody = BaseActionSchema.merge( - z.object({ - parameters: z.object({ - path: z.string(), - }), - }) -); diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/state/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/state/index.ts new file mode 100644 index 0000000000000..38db865a0e2c7 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/state/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 './state.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/state/state.gen.ts similarity index 59% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/file_download.gen.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/state/state.gen.ts index e670d2070d8ab..758835f484034 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_download.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/state/state.gen.ts @@ -10,14 +10,13 @@ * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. * * info: - * title: File Download Schema + * title: Endpoint Action State Schema * version: 2023-10-31 */ -import { z } from 'zod'; +import type { z } from 'zod'; -export type FileDownloadRequestParams = z.infer<typeof FileDownloadRequestParams>; -export const FileDownloadRequestParams = z.object({ - action_id: z.string(), - file_id: z.string(), -}); +import { SuccessResponse } from '../../model/schema/common.gen'; + +export type EndpointGetActionsStateResponse = z.infer<typeof EndpointGetActionsStateResponse>; +export const EndpointGetActionsStateResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/state/state.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/state/state.schema.yaml new file mode 100644 index 0000000000000..a8d9187107cbe --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/state/state.schema.yaml @@ -0,0 +1,19 @@ +openapi: 3.0.0 +info: + title: Endpoint Action State Schema + version: '2023-10-31' +paths: + /api/endpoint/action/state: + get: + summary: Get Action State schema + operationId: EndpointGetActionsState + x-codegen-enabled: true + x-labels: [ess, serverless] + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/status/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/status/index.ts new file mode 100644 index 0000000000000..bcbee2a39039b --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/status/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 './status'; +export * from './status.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.gen.ts new file mode 100644 index 0000000000000..69918d650e4a0 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Get Action status schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { AgentIds, SuccessResponse } from '../../model/schema/common.gen'; + +export type EndpointGetActionsStatusRequestQuery = z.infer< + typeof EndpointGetActionsStatusRequestQuery +>; +export const EndpointGetActionsStatusRequestQuery = z.object({ + query: z.object({ + agent_ids: AgentIds.optional(), + }), +}); +export type EndpointGetActionsStatusRequestQueryInput = z.input< + typeof EndpointGetActionsStatusRequestQuery +>; + +export type EndpointGetActionsStatusResponse = z.infer<typeof EndpointGetActionsStatusResponse>; +export const EndpointGetActionsStatusResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/actions_status.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.schema.yaml similarity index 64% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/actions_status.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.schema.yaml index 92438f6d1a9fc..ec8e3d386bcd2 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/actions_status.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.schema.yaml @@ -7,10 +7,9 @@ paths: get: summary: Get Actions status schema operationId: EndpointGetActionsStatus - x-codegen-enabled: false - x-labels: - - ess - - serverless + description: Get action status + x-codegen-enabled: true + x-labels: [ess, serverless] parameters: - name: query in: query @@ -19,12 +18,12 @@ paths: type: object properties: agent_ids: - $ref: '../model/schema/common.schema.yaml#/components/schemas/AgentIds' + $ref: '../../model/schema/common.schema.yaml#/components/schemas/AgentIds' responses: '200': description: OK content: application/json: schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' + $ref: '../../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/action_status_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/actions/action_status_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/actions/status/status.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/index.ts index 6dfbcd20d12fb..5917101be93a1 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/index.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/index.ts @@ -5,28 +5,28 @@ * 2.0. */ -export * from './actions/audit_log_route'; -export * from './actions/action_status_route'; -export * from './actions/details_route'; -export * from './actions/file_download_route'; -export * from './actions/file_info_route'; -export * from './actions/file_upload_route'; -export * from './actions/list_route'; -export * from './actions/isolate_route'; -export * from './actions/unisolate_route'; -export * from './actions/kill_process_route'; -export * from './actions/suspend_process_route'; -export * from './actions/get_processes_route'; -export * from './actions/get_file_route'; -export * from './actions/execute_route'; -export * from './actions/scan_route'; export * from './actions/common/base'; export * from './actions/common/response_actions'; -export * from './metadata/list_metadata_route'; -export * from './metadata/get_metadata_route'; +export * from './actions/action_log'; +export * from './actions/status'; +export * from './actions/details'; +export * from './actions/file_download'; +export * from './actions/file_info'; +export * from './actions/list'; -export * from './policy/get_policy_response_route'; -export * from './policy/get_agent_policy_summary_route'; +export * from './actions/response_actions/isolate'; +export * from './actions/response_actions/unisolate'; +export * from './actions/response_actions/kill_process'; +export * from './actions/response_actions/suspend_process'; +export * from './actions/response_actions/running_procs'; +export * from './actions/response_actions/get_file'; +export * from './actions/response_actions/execute'; +export * from './actions/response_actions/upload'; +export * from './actions/response_actions/scan'; -export * from './suggestions/get_suggestions_route'; +export * from './metadata'; + +export * from './policy'; + +export * from './suggestions'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.gen.ts new file mode 100644 index 0000000000000..a520b56c07195 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Endpoint Metadata Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { ListRequestQuery } from './list_metadata.gen'; +import { SuccessResponse } from '../model/schema/common.gen'; + +export type GetEndpointMetadataListRequestQuery = z.infer< + typeof GetEndpointMetadataListRequestQuery +>; +export const GetEndpointMetadataListRequestQuery = z.object({ + query: ListRequestQuery, +}); +export type GetEndpointMetadataListRequestQueryInput = z.input< + typeof GetEndpointMetadataListRequestQuery +>; + +export type GetEndpointMetadataListResponse = z.infer<typeof GetEndpointMetadataListResponse>; +export const GetEndpointMetadataListResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/metadata/metadata.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.schema.yaml similarity index 82% rename from x-pack/plugins/security_solution/common/api/endpoint/metadata/metadata.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.schema.yaml index a8af6101472d3..5ecea044053d2 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/metadata/metadata.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.schema.yaml @@ -7,10 +7,8 @@ paths: get: summary: Get Metadata List schema operationId: GetEndpointMetadataList - x-codegen-enabled: false - x-labels: - - ess - - serverless + x-codegen-enabled: true + x-labels: [ess, serverless] parameters: - name: query in: query @@ -30,9 +28,7 @@ paths: summary: Get Metadata Transform schema operationId: GetEndpointMetadataTransform x-codegen-enabled: false - x-labels: - - ess - - serverless + x-labels: [ess, serverless] responses: '200': description: OK @@ -46,18 +42,13 @@ paths: summary: Get Metadata schema operationId: GetEndpointMetadata x-codegen-enabled: false - x-labels: - - ess - - serverless + x-labels: [ess, serverless] parameters: - - name: query + - name: id in: path required: true schema: - type: object - properties: - id: - type: string + type: string responses: '200': description: OK diff --git a/x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/metadata/get_metadata.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/metadata/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/metadata/index.ts new file mode 100644 index 0000000000000..d95135ef8aa90 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/metadata/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './get_metadata'; +export * from './get_metadata.gen'; + +export * from './list_metadata'; +export * from './list_metadata.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/metadata/list_metadata_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/metadata/list_metadata.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/metadata/list_metadata_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/metadata/list_metadata.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts index 4e3885391f241..57af5c334f4f0 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts @@ -70,6 +70,7 @@ export const Command = z.enum([ 'get-file', 'execute', 'upload', + 'scan', ]); export type CommandEnum = typeof Command.enum; export const CommandEnum = Command.enum; @@ -98,16 +99,22 @@ export type UserIds = z.infer<typeof UserIds>; export const UserIds = z.union([z.array(z.string().min(1)).min(1), z.string().min(1)]); /** - * With Outputs + * Shows detailed outputs for an action response */ export type WithOutputs = z.infer<typeof WithOutputs>; export const WithOutputs = z.union([z.array(z.string().min(1)).min(1), z.string().min(1)]); +/** + * Type of response action + */ export type Type = z.infer<typeof Type>; export const Type = z.enum(['automated', 'manual']); export type TypeEnum = typeof Type.enum; export const TypeEnum = Type.enum; +/** + * List of types of response actions + */ export type Types = z.infer<typeof Types>; export const Types = z.array(Type); @@ -135,17 +142,28 @@ export const Comment = z.string(); export type Parameters = z.infer<typeof Parameters>; export const Parameters = z.object({}); +export type AgentTypes = z.infer<typeof AgentTypes>; +export const AgentTypes = z.enum(['endpoint', 'sentinel_one', 'crowdstrike']); +export type AgentTypesEnum = typeof AgentTypes.enum; +export const AgentTypesEnum = AgentTypes.enum; + export type BaseActionSchema = z.infer<typeof BaseActionSchema>; export const BaseActionSchema = z.object({ - endpoint_ids: EndpointIds.optional(), + endpoint_ids: EndpointIds, alert_ids: AlertIds.optional(), case_ids: CaseIds.optional(), comment: Comment.optional(), parameters: Parameters.optional(), + agent_type: AgentTypes.optional(), +}); + +export type NoParametersRequestSchema = z.infer<typeof NoParametersRequestSchema>; +export const NoParametersRequestSchema = z.object({ + body: BaseActionSchema, }); -export type ProcessActionSchemas = z.infer<typeof ProcessActionSchemas>; -export const ProcessActionSchemas = BaseActionSchema.merge( +export type KillOrSuspendActionSchema = z.infer<typeof KillOrSuspendActionSchema>; +export const KillOrSuspendActionSchema = BaseActionSchema.merge( z.object({ parameters: z.union([ z.object({ diff --git a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml index 306bd31f4886b..00cf557b98496 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.schema.yaml @@ -54,6 +54,7 @@ components: - get-file - execute - upload + - scan minLength: 1 description: The command to be executed (cannot be an empty string) @@ -101,16 +102,18 @@ components: minItems: 1 - type: string minLength: 1 - description: With Outputs + description: Shows detailed outputs for an action response Type: type: string + description: Type of response action enum: - automated - manual Types: type: array + description: List of types of response actions items: $ref: '#/components/schemas/Type' minLength: 1 @@ -136,6 +139,12 @@ components: Parameters: type: object description: Optional parameters object + AgentTypes: + type: string + enum: + - endpoint + - sentinel_one + - crowdstrike BaseActionSchema: x-inline: true @@ -151,8 +160,20 @@ components: $ref: '#/components/schemas/Comment' parameters: $ref: '#/components/schemas/Parameters' + agent_type: + $ref: '#/components/schemas/AgentTypes' + required: + - endpoint_ids + + NoParametersRequestSchema: + type: object + required: + - body + properties: + body: + $ref: '#/components/schemas/BaseActionSchema' - ProcessActionSchemas: + KillOrSuspendActionSchema: allOf: - $ref: '#/components/schemas/BaseActionSchema' - type: object diff --git a/x-pack/plugins/security_solution/common/api/endpoint/policy/policy.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.gen.ts similarity index 66% rename from x-pack/plugins/security_solution/common/api/endpoint/policy/policy.gen.ts rename to x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.gen.ts index 9cc37e874f6a2..9a20dcfc3b310 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/policy/policy.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.gen.ts @@ -16,7 +16,7 @@ import { z } from 'zod'; -import { SuccessResponse, AgentId } from '../model/schema/common.gen'; +import { SuccessResponse } from '../model/schema/common.gen'; export type GetAgentPolicySummaryRequestQuery = z.infer<typeof GetAgentPolicySummaryRequestQuery>; export const GetAgentPolicySummaryRequestQuery = z.object({ @@ -31,13 +31,3 @@ export type GetAgentPolicySummaryRequestQueryInput = z.input< export type GetAgentPolicySummaryResponse = z.infer<typeof GetAgentPolicySummaryResponse>; export const GetAgentPolicySummaryResponse = SuccessResponse; -export type GetPolicyResponseRequestQuery = z.infer<typeof GetPolicyResponseRequestQuery>; -export const GetPolicyResponseRequestQuery = z.object({ - query: z.object({ - agentId: AgentId.optional(), - }), -}); -export type GetPolicyResponseRequestQueryInput = z.input<typeof GetPolicyResponseRequestQuery>; - -export type GetPolicyResponseResponse = z.infer<typeof GetPolicyResponseResponse>; -export const GetPolicyResponseResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/policy/policy.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.schema.yaml similarity index 50% rename from x-pack/plugins/security_solution/common/api/endpoint/policy/policy.schema.yaml rename to x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.schema.yaml index 6084ae851bc17..803010a4e8268 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/policy/policy.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.schema.yaml @@ -7,10 +7,9 @@ paths: get: summary: Get Agent Policy Summary schema operationId: GetAgentPolicySummary + deprecated: true x-codegen-enabled: true - x-labels: - - ess - - serverless + x-labels: [ess, serverless] parameters: - name: query in: query @@ -31,28 +30,3 @@ paths: application/json: schema: $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' - - /api/endpoint/policy_response: - get: - summary: Get Policy Response schema - operationId: GetPolicyResponse - x-codegen-enabled: true - x-labels: - - ess - - serverless - parameters: - - name: query - in: query - required: true - schema: - type: object - properties: - agentId: - $ref: '../model/schema/common.schema.yaml#/components/schemas/AgentId' - responses: - '200': - description: OK - content: - application/json: - schema: - $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/policy/get_agent_policy_summary_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/policy/get_agent_policy_summary_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/policy/deprecated_agent_policy_summary.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/policy/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/policy/index.ts new file mode 100644 index 0000000000000..e27dc83a3b993 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/policy/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 './policy_response'; +export * from './deprecated_agent_policy_summary'; +export * from './policy_response.gen'; +export * from './deprecated_agent_policy_summary.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.gen.ts new file mode 100644 index 0000000000000..e2eb2595be662 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.gen.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. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Endpoint Policy Schema + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { AgentId, SuccessResponse } from '../model/schema/common.gen'; + +export type GetPolicyResponseRequestQuery = z.infer<typeof GetPolicyResponseRequestQuery>; +export const GetPolicyResponseRequestQuery = z.object({ + query: z.object({ + agentId: AgentId.optional(), + }), +}); +export type GetPolicyResponseRequestQueryInput = z.input<typeof GetPolicyResponseRequestQuery>; + +export type GetPolicyResponseResponse = z.infer<typeof GetPolicyResponseResponse>; +export const GetPolicyResponseResponse = SuccessResponse; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.schema.yaml new file mode 100644 index 0000000000000..7acc39013da85 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.schema.yaml @@ -0,0 +1,27 @@ +openapi: 3.0.0 +info: + title: Endpoint Policy Schema + version: '2023-10-31' +paths: + /api/endpoint/policy_response: + get: + summary: Get Policy Response schema + operationId: GetPolicyResponse + x-codegen-enabled: true + x-labels: [ess, serverless] + parameters: + - name: query + in: query + required: true + schema: + type: object + properties: + agentId: + $ref: '../model/schema/common.schema.yaml#/components/schemas/AgentId' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '../model/schema/common.schema.yaml#/components/schemas/SuccessResponse' diff --git a/x-pack/plugins/security_solution/common/api/endpoint/policy/get_policy_response_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/policy/get_policy_response_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/policy/policy_response.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/index.ts new file mode 100644 index 0000000000000..c49eecd853e2c --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/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 './protection_updates_note'; +export * from './protection_updates_note.gen'; diff --git a/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note.schema.yaml index ce2760780b627..44c02e417b185 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note.schema.yaml @@ -8,9 +8,7 @@ paths: summary: Get Protection Updates Note schema operationId: GetProtectionUpdatesNote x-codegen-enabled: true - x-labels: - - ess - - serverless + x-labels: [ess, serverless] parameters: - name: package_policy_id in: path @@ -28,9 +26,7 @@ paths: summary: Create Update Protection Updates Note schema operationId: CreateUpdateProtectionUpdatesNote x-codegen-enabled: true - x-labels: - - ess - - serverless + x-labels: [ess, serverless] requestBody: required: true content: diff --git a/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note_schema.ts b/x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note_schema.ts rename to x-pack/plugins/security_solution/common/api/endpoint/protection_updates_note/protection_updates_note.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions.schema.yaml b/x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions.schema.yaml index b7e1b0f34780e..573f9c0e3992f 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions.schema.yaml @@ -8,9 +8,7 @@ paths: summary: Get suggestions operationId: GetEndpointSuggestions x-codegen-enabled: true - x-labels: - - ess - - serverless + x-labels: [ess, serverless] requestBody: required: true content: diff --git a/x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions_route.ts b/x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions.ts similarity index 100% rename from x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions_route.ts rename to x-pack/plugins/security_solution/common/api/endpoint/suggestions/get_suggestions.ts diff --git a/x-pack/plugins/security_solution/common/api/endpoint/suggestions/index.ts b/x-pack/plugins/security_solution/common/api/endpoint/suggestions/index.ts new file mode 100644 index 0000000000000..a4b3e85842ae9 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/endpoint/suggestions/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 './get_suggestions'; +export * from './get_suggestions.gen'; diff --git a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.ts b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.ts index 453f7ff045df2..219632fc522e9 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.ts @@ -10,8 +10,8 @@ import { unionWithNullType } from '../../../utility_types'; export const getNotesSchema = runtimeTypes.partial({ documentIds: runtimeTypes.union([runtimeTypes.array(runtimeTypes.string), runtimeTypes.string]), - page: unionWithNullType(runtimeTypes.number), - perPage: unionWithNullType(runtimeTypes.number), + page: unionWithNullType(runtimeTypes.string), + perPage: unionWithNullType(runtimeTypes.string), search: unionWithNullType(runtimeTypes.string), sortField: unionWithNullType(runtimeTypes.string), sortOrder: unionWithNullType(runtimeTypes.string), diff --git a/x-pack/plugins/security_solution/common/api/timeline/index.ts b/x-pack/plugins/security_solution/common/api/timeline/index.ts index 6229b07c53a9f..c2901b96417db 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/api/timeline/index.ts @@ -21,3 +21,4 @@ export * from './persist_note/persist_note_route'; export * from './pinned_events/pinned_events_route'; export * from './install_prepackaged_timelines/install_prepackaged_timelines'; export * from './copy_timeline/copy_timeline_route'; +export * from './get_notes/get_notes_route'; diff --git a/x-pack/plugins/security_solution/common/detection_engine/constants.ts b/x-pack/plugins/security_solution/common/detection_engine/constants.ts index 8e06f46f1f46d..7057e3c8b3091 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/constants.ts @@ -29,6 +29,7 @@ export enum RULE_PREVIEW_FROM { } export const PREBUILT_RULES_PACKAGE_NAME = 'security_detection_engine'; +export const ENDPOINT_PACKAGE_NAME = 'endpoint'; /** * Rule signature id (`rule.rule_id`) of the prebuilt "Endpoint Security" rule. diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts index 4ea9f59ea179d..c7ce775bb2129 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/actions.test.ts @@ -19,10 +19,10 @@ import { KillProcessRouteRequestSchema, SuspendProcessRouteRequestSchema, UploadActionRequestSchema, + ExecuteActionRequestSchema, + ScanActionRequestSchema, + NoParametersRequestSchema, } from '../../api/endpoint'; -import { NoParametersRequestSchema } from '../../api/endpoint/actions/common/base'; -import { ExecuteActionRequestSchema } from '../../api/endpoint/actions/execute_route'; -import { ScanActionRequestSchema } from '../../api/endpoint/actions/scan_route'; // NOTE: Even though schemas are kept in common/api/endpoint - we keep tests here, because common/api should import from outside describe('actions schemas', () => { diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index 47556a966280a..061182d5075ac 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -9,13 +9,12 @@ import type { TypeOf } from '@kbn/config-schema'; import type { EcsError } from '@elastic/ecs'; import type { BaseFileMetadata, FileCompression, FileJSON } from '@kbn/files-plugin/common'; import type { - KillProcessRouteRequestSchema, - ResponseActionBodySchema, - SuspendProcessRouteRequestSchema, UploadActionApiRequestBody, + ActionStatusRequestSchema, + KillProcessRequestBody, + SuspendProcessRequestBody, } from '../../api/endpoint'; -import type { ActionStatusRequestSchema } from '../../api/endpoint/actions/action_status_route'; -import type { NoParametersRequestSchema } from '../../api/endpoint/actions/common/base'; + import type { ResponseActionAgentType, ResponseActionsApiCommandNames, @@ -358,14 +357,6 @@ export interface ActivityLog { data: ActivityLogEntry[]; } -export type HostIsolationRequestBody = TypeOf<typeof NoParametersRequestSchema.body>; - -export type ResponseActionRequestBody = TypeOf<typeof ResponseActionBodySchema>; - -export type KillProcessRequestBody = TypeOf<typeof KillProcessRouteRequestSchema.body>; - -export type SuspendProcessRequestBody = TypeOf<typeof SuspendProcessRouteRequestSchema.body>; - /** Note: this type should almost never be used. Use instead the response action specific types above */ export type KillOrSuspendProcessRequestBody = KillProcessRequestBody & SuspendProcessRequestBody; @@ -373,8 +364,6 @@ export interface HostIsolationResponse { action: string; } -export type ProcessesRequestBody = TypeOf<typeof NoParametersRequestSchema.body>; - export interface ResponseActionApiResponse< TOutputContent extends EndpointActionResponseDataOutput = EndpointActionResponseDataOutput > { diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml index 93e0b5298c4aa..f291b2db216c0 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml @@ -13,13 +13,14 @@ servers: paths: /api/endpoint/action: get: + description: Get a list of action requests and their responses operationId: EndpointGetActionsList parameters: - in: query name: query required: true schema: - $ref: '#/components/schemas/EndpointActionListRequestQuery' + $ref: '#/components/schemas/GetEndpointActionListRouteQuery' responses: '200': content: @@ -32,18 +33,20 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action_log/{agent_id}': get: - operationId: EndpointGetActionAuditLog + deprecated: true + description: Get action requests log + operationId: EndpointGetActionLog parameters: - - in: query - name: query + - in: path + name: agent_id required: true schema: - $ref: '#/components/schemas/AuditLogRequestQuery' - - in: path + $ref: '#/components/schemas/AgentId' + - in: query name: query required: true schema: - $ref: '#/components/schemas/AuditLogRequestParams' + $ref: '#/components/schemas/ActionLogRequestQuery' responses: '200': content: @@ -51,11 +54,12 @@ paths: schema: $ref: '#/components/schemas/SuccessResponse' description: OK - summary: Get action audit log schema + summary: Get action requests log schema tags: - Security Solution Endpoint Management API /api/endpoint/action_status: get: + description: Get action status operationId: EndpointGetActionsStatus parameters: - in: query @@ -78,13 +82,14 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action/{action_id}': get: + description: Get action details operationId: EndpointGetActionsDetails parameters: - in: path - name: query + name: action_id required: true schema: - $ref: '#/components/schemas/DetailsRequestParams' + type: string responses: '200': content: @@ -97,13 +102,19 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action/{action_id}/file/{file_id}/download`': get: + description: Download a file from an endpoint operationId: EndpointFileDownload parameters: - in: path - name: query + name: action_id required: true schema: - $ref: '#/components/schemas/FileDownloadRequestParams' + type: string + - in: path + name: file_id + required: true + schema: + type: string responses: '200': content: @@ -116,13 +127,19 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action/{action_id}/file/{file_id}`': get: + description: Get file info operationId: EndpointFileInfo parameters: - in: path - name: query + name: action_id required: true schema: - $ref: '#/components/schemas/FileInfoRequestParams' + type: string + - in: path + name: file_id + required: true + schema: + type: string responses: '200': content: @@ -135,12 +152,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/execute: post: + description: Execute a given command on an endpoint operationId: EndpointExecuteAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ExecuteActionRequestBody' + $ref: '#/components/schemas/ExecuteRouteRequestBody' required: true responses: '200': @@ -154,12 +172,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/get_file: post: + description: Get a file from an endpoint operationId: EndpointGetFileAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/GetFileActionRequestBody' + $ref: '#/components/schemas/GetFileRouteRequestBody' required: true responses: '200': @@ -173,23 +192,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/isolate: post: - operationId: EndpointIsolateHostAction + description: Isolate an endpoint + operationId: EndpointIsolateAction requestBody: content: application/json: schema: - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' + $ref: '#/components/schemas/IsolateRouteRequestBody' required: true responses: '200': @@ -198,17 +207,18 @@ paths: schema: $ref: '#/components/schemas/SuccessResponse' description: OK - summary: Isolate host Action + summary: Isolate Action tags: - Security Solution Endpoint Management API /api/endpoint/action/kill_process: post: + description: Kill a running process on an endpoint operationId: EndpointKillProcessAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ProcessActionSchemas' + $ref: '#/components/schemas/KillOrSuspendActionSchema' required: true responses: '200': @@ -222,23 +232,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/running_procs: post: - operationId: EndpointGetRunningProcessesAction + description: Get list of running processes on an endpoint + operationId: EndpointGetProcessesAction requestBody: content: application/json: schema: - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' + $ref: '#/components/schemas/GetProcessesRouteRequestBody' required: true responses: '200': @@ -252,12 +252,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/scan: post: + description: Scan a file or directory operationId: EndpointScanAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ScanActionRequestBody' + $ref: '#/components/schemas/ScanRouteRequestBody' required: true responses: '200': @@ -284,12 +285,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/suspend_process: post: + description: Suspend a running process on an endpoint operationId: EndpointSuspendProcessAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ProcessActionSchemas' + $ref: '#/components/schemas/KillOrSuspendActionSchema' required: true responses: '200': @@ -303,23 +305,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/unisolate: post: - operationId: EndpointUnisolateHostAction + description: Release an endpoint + operationId: EndpointUnisolateAction requestBody: content: application/json: schema: - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' + $ref: '#/components/schemas/UnisolateRouteRequestBody' required: true responses: '200': @@ -328,17 +320,18 @@ paths: schema: $ref: '#/components/schemas/SuccessResponse' description: OK - summary: Unisolate host Action + summary: Unisolate Action tags: - Security Solution Endpoint Management API /api/endpoint/action/upload: post: + description: Upload a file to an endpoint operationId: EndpointUploadAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/FileUploadActionRequestBody' + $ref: '#/components/schemas/UploadRouteRequestBody' required: true responses: '200': @@ -352,6 +345,7 @@ paths: - Security Solution Endpoint Management API /api/endpoint/isolate: post: + deprecated: true operationId: EndpointIsolateRedirect requestBody: content: @@ -359,6 +353,8 @@ paths: schema: type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -369,6 +365,8 @@ paths: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids required: true responses: '200': @@ -412,13 +410,10 @@ paths: operationId: GetEndpointMetadata parameters: - in: path - name: query + name: id required: true schema: - type: object - properties: - id: - type: string + type: string responses: '200': content: @@ -466,6 +461,7 @@ paths: - Security Solution Endpoint Management API /api/endpoint/policy/summaries: get: + deprecated: true operationId: GetAgentPolicySummary parameters: - in: query @@ -573,6 +569,7 @@ paths: - Security Solution Endpoint Management API /api/endpoint/unisolate: post: + deprecated: true operationId: EndpointUnisolateRedirect requestBody: content: @@ -580,6 +577,8 @@ paths: schema: type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -590,6 +589,8 @@ paths: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids required: true responses: '200': @@ -611,6 +612,17 @@ paths: - Security Solution Endpoint Management API components: schemas: + ActionLogRequestQuery: + type: object + properties: + end_date: + $ref: '#/components/schemas/EndDate' + page: + $ref: '#/components/schemas/Page' + page_size: + $ref: '#/components/schemas/PageSize' + start_date: + $ref: '#/components/schemas/StartDate' AgentId: description: Agent ID type: string @@ -625,28 +637,18 @@ components: type: array - minLength: 1 type: string + AgentTypes: + enum: + - endpoint + - sentinel_one + - crowdstrike + type: string AlertIds: description: A list of alerts ids. items: $ref: '#/components/schemas/NonEmptyString' minItems: 1 type: array - AuditLogRequestParams: - type: object - properties: - agent_id: - $ref: '#/components/schemas/AgentId' - AuditLogRequestQuery: - type: object - properties: - end_date: - $ref: '#/components/schemas/EndDate' - page: - $ref: '#/components/schemas/Page' - page_size: - $ref: '#/components/schemas/PageSize' - start_date: - $ref: '#/components/schemas/StartDate' CaseIds: description: Case IDs to be updated (cannot contain empty strings) items: @@ -665,6 +667,7 @@ components: - get-file - execute - upload + - scan minLength: 1 type: string Commands: @@ -674,39 +677,9 @@ components: Comment: description: Optional comment type: string - DetailsRequestParams: - type: object - properties: - action_id: - type: string EndDate: description: End date type: string - EndpointActionListRequestQuery: - type: object - properties: - agentIds: - $ref: '#/components/schemas/AgentIds' - commands: - $ref: '#/components/schemas/Commands' - endDate: - $ref: '#/components/schemas/EndDate' - page: - $ref: '#/components/schemas/Page' - pageSize: - default: 10 - description: Number of items per page - maximum: 10000 - minimum: 1 - type: integer - startDate: - $ref: '#/components/schemas/StartDate' - types: - $ref: '#/components/schemas/Types' - userIds: - $ref: '#/components/schemas/UserIds' - withOutputs: - $ref: '#/components/schemas/WithOutputs' EndpointIds: description: List of endpoint IDs (cannot contain empty strings) items: @@ -714,10 +687,12 @@ components: type: string minItems: 1 type: array - ExecuteActionRequestBody: + ExecuteRouteRequestBody: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -728,6 +703,8 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: parameters: @@ -741,30 +718,39 @@ components: - command required: - parameters - FileDownloadRequestParams: + GetEndpointActionListRouteQuery: type: object properties: - action_id: - type: string - file_id: - type: string - required: - - action_id - - file_id - FileInfoRequestParams: - type: object - properties: - action_id: - type: string - file_id: - type: string - required: - - action_id - - file_id - FileUploadActionRequestBody: + agentIds: + $ref: '#/components/schemas/AgentIds' + agentTypes: + $ref: '#/components/schemas/AgentTypes' + commands: + $ref: '#/components/schemas/Commands' + endDate: + $ref: '#/components/schemas/EndDate' + page: + $ref: '#/components/schemas/Page' + pageSize: + default: 10 + description: Number of items per page + maximum: 10000 + minimum: 1 + type: integer + startDate: + $ref: '#/components/schemas/StartDate' + types: + $ref: '#/components/schemas/Types' + userIds: + $ref: '#/components/schemas/UserIds' + withOutputs: + $ref: '#/components/schemas/WithOutputs' + GetFileRouteRequestBody: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -775,24 +761,29 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: - file: - format: binary - type: string parameters: type: object properties: - overwrite: - default: false - type: boolean + path: + type: string + required: + - path required: - parameters - - file - GetFileActionRequestBody: + GetProcessesRouteRequestBody: + $ref: '#/components/schemas/NoParametersRequestSchema' + IsolateRouteRequestBody: + $ref: '#/components/schemas/NoParametersRequestSchema' + KillOrSuspendActionSchema: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -803,15 +794,22 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: parameters: - type: object - properties: - path: - type: string - required: - - path + oneOf: + - type: object + properties: + pid: + minimum: 1 + type: integer + - type: object + properties: + entity_id: + minLength: 1 + type: string required: - parameters ListRequestQuery: @@ -866,6 +864,28 @@ components: minLength: 1 pattern: ^(?! *$).+$ type: string + NoParametersRequestSchema: + type: object + properties: + body: + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids + required: + - body Page: default: 1 description: Page number @@ -880,45 +900,17 @@ components: Parameters: description: Optional parameters object type: object - ProcessActionSchemas: - allOf: - - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' - - type: object - properties: - parameters: - oneOf: - - type: object - properties: - pid: - minimum: 1 - type: integer - - type: object - properties: - entity_id: - minLength: 1 - type: string - required: - - parameters ProtectionUpdatesNoteResponse: type: object properties: note: type: string - ScanActionRequestBody: + ScanRouteRequestBody: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -929,6 +921,8 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: parameters: @@ -951,16 +945,52 @@ components: minimum: 1 type: integer Type: + description: Type of response action enum: - automated - manual type: string Types: + description: List of types of response actions items: $ref: '#/components/schemas/Type' maxLength: 2 minLength: 1 type: array + UnisolateRouteRequestBody: + $ref: '#/components/schemas/NoParametersRequestSchema' + UploadRouteRequestBody: + allOf: + - type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids + - type: object + properties: + file: + format: binary + type: string + parameters: + type: object + properties: + overwrite: + default: false + type: boolean + required: + - parameters + - file UserIds: description: User IDs oneOf: @@ -972,7 +1002,7 @@ components: - minLength: 1 type: string WithOutputs: - description: With Outputs + description: Shows detailed outputs for an action response oneOf: - items: minLength: 1 diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml index 76bf0dede41b7..82af84f6d4253 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml @@ -13,13 +13,14 @@ servers: paths: /api/endpoint/action: get: + description: Get a list of action requests and their responses operationId: EndpointGetActionsList parameters: - in: query name: query required: true schema: - $ref: '#/components/schemas/EndpointActionListRequestQuery' + $ref: '#/components/schemas/GetEndpointActionListRouteQuery' responses: '200': content: @@ -32,18 +33,20 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action_log/{agent_id}': get: - operationId: EndpointGetActionAuditLog + deprecated: true + description: Get action requests log + operationId: EndpointGetActionLog parameters: - - in: query - name: query + - in: path + name: agent_id required: true schema: - $ref: '#/components/schemas/AuditLogRequestQuery' - - in: path + $ref: '#/components/schemas/AgentId' + - in: query name: query required: true schema: - $ref: '#/components/schemas/AuditLogRequestParams' + $ref: '#/components/schemas/ActionLogRequestQuery' responses: '200': content: @@ -51,11 +54,12 @@ paths: schema: $ref: '#/components/schemas/SuccessResponse' description: OK - summary: Get action audit log schema + summary: Get action requests log schema tags: - Security Solution Endpoint Management API /api/endpoint/action_status: get: + description: Get action status operationId: EndpointGetActionsStatus parameters: - in: query @@ -78,13 +82,14 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action/{action_id}': get: + description: Get action details operationId: EndpointGetActionsDetails parameters: - in: path - name: query + name: action_id required: true schema: - $ref: '#/components/schemas/DetailsRequestParams' + type: string responses: '200': content: @@ -97,13 +102,19 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action/{action_id}/file/{file_id}/download`': get: + description: Download a file from an endpoint operationId: EndpointFileDownload parameters: - in: path - name: query + name: action_id required: true schema: - $ref: '#/components/schemas/FileDownloadRequestParams' + type: string + - in: path + name: file_id + required: true + schema: + type: string responses: '200': content: @@ -116,13 +127,19 @@ paths: - Security Solution Endpoint Management API '/api/endpoint/action/{action_id}/file/{file_id}`': get: + description: Get file info operationId: EndpointFileInfo parameters: - in: path - name: query + name: action_id required: true schema: - $ref: '#/components/schemas/FileInfoRequestParams' + type: string + - in: path + name: file_id + required: true + schema: + type: string responses: '200': content: @@ -135,12 +152,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/execute: post: + description: Execute a given command on an endpoint operationId: EndpointExecuteAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ExecuteActionRequestBody' + $ref: '#/components/schemas/ExecuteRouteRequestBody' required: true responses: '200': @@ -154,12 +172,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/get_file: post: + description: Get a file from an endpoint operationId: EndpointGetFileAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/GetFileActionRequestBody' + $ref: '#/components/schemas/GetFileRouteRequestBody' required: true responses: '200': @@ -173,23 +192,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/isolate: post: - operationId: EndpointIsolateHostAction + description: Isolate an endpoint + operationId: EndpointIsolateAction requestBody: content: application/json: schema: - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' + $ref: '#/components/schemas/IsolateRouteRequestBody' required: true responses: '200': @@ -198,17 +207,18 @@ paths: schema: $ref: '#/components/schemas/SuccessResponse' description: OK - summary: Isolate host Action + summary: Isolate Action tags: - Security Solution Endpoint Management API /api/endpoint/action/kill_process: post: + description: Kill a running process on an endpoint operationId: EndpointKillProcessAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ProcessActionSchemas' + $ref: '#/components/schemas/KillOrSuspendActionSchema' required: true responses: '200': @@ -222,23 +232,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/running_procs: post: - operationId: EndpointGetRunningProcessesAction + description: Get list of running processes on an endpoint + operationId: EndpointGetProcessesAction requestBody: content: application/json: schema: - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' + $ref: '#/components/schemas/GetProcessesRouteRequestBody' required: true responses: '200': @@ -252,12 +252,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/scan: post: + description: Scan a file or directory operationId: EndpointScanAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ScanActionRequestBody' + $ref: '#/components/schemas/ScanRouteRequestBody' required: true responses: '200': @@ -284,12 +285,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/suspend_process: post: + description: Suspend a running process on an endpoint operationId: EndpointSuspendProcessAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/ProcessActionSchemas' + $ref: '#/components/schemas/KillOrSuspendActionSchema' required: true responses: '200': @@ -303,23 +305,13 @@ paths: - Security Solution Endpoint Management API /api/endpoint/action/unisolate: post: - operationId: EndpointUnisolateHostAction + description: Release an endpoint + operationId: EndpointUnisolateAction requestBody: content: application/json: schema: - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' + $ref: '#/components/schemas/UnisolateRouteRequestBody' required: true responses: '200': @@ -328,17 +320,18 @@ paths: schema: $ref: '#/components/schemas/SuccessResponse' description: OK - summary: Unisolate host Action + summary: Unisolate Action tags: - Security Solution Endpoint Management API /api/endpoint/action/upload: post: + description: Upload a file to an endpoint operationId: EndpointUploadAction requestBody: content: application/json: schema: - $ref: '#/components/schemas/FileUploadActionRequestBody' + $ref: '#/components/schemas/UploadRouteRequestBody' required: true responses: '200': @@ -374,13 +367,10 @@ paths: operationId: GetEndpointMetadata parameters: - in: path - name: query + name: id required: true schema: - type: object - properties: - id: - type: string + type: string responses: '200': content: @@ -428,6 +418,7 @@ paths: - Security Solution Endpoint Management API /api/endpoint/policy/summaries: get: + deprecated: true operationId: GetAgentPolicySummary parameters: - in: query @@ -535,6 +526,17 @@ paths: - Security Solution Endpoint Management API components: schemas: + ActionLogRequestQuery: + type: object + properties: + end_date: + $ref: '#/components/schemas/EndDate' + page: + $ref: '#/components/schemas/Page' + page_size: + $ref: '#/components/schemas/PageSize' + start_date: + $ref: '#/components/schemas/StartDate' AgentId: description: Agent ID type: string @@ -549,28 +551,18 @@ components: type: array - minLength: 1 type: string + AgentTypes: + enum: + - endpoint + - sentinel_one + - crowdstrike + type: string AlertIds: description: A list of alerts ids. items: $ref: '#/components/schemas/NonEmptyString' minItems: 1 type: array - AuditLogRequestParams: - type: object - properties: - agent_id: - $ref: '#/components/schemas/AgentId' - AuditLogRequestQuery: - type: object - properties: - end_date: - $ref: '#/components/schemas/EndDate' - page: - $ref: '#/components/schemas/Page' - page_size: - $ref: '#/components/schemas/PageSize' - start_date: - $ref: '#/components/schemas/StartDate' CaseIds: description: Case IDs to be updated (cannot contain empty strings) items: @@ -589,6 +581,7 @@ components: - get-file - execute - upload + - scan minLength: 1 type: string Commands: @@ -598,39 +591,9 @@ components: Comment: description: Optional comment type: string - DetailsRequestParams: - type: object - properties: - action_id: - type: string EndDate: description: End date type: string - EndpointActionListRequestQuery: - type: object - properties: - agentIds: - $ref: '#/components/schemas/AgentIds' - commands: - $ref: '#/components/schemas/Commands' - endDate: - $ref: '#/components/schemas/EndDate' - page: - $ref: '#/components/schemas/Page' - pageSize: - default: 10 - description: Number of items per page - maximum: 10000 - minimum: 1 - type: integer - startDate: - $ref: '#/components/schemas/StartDate' - types: - $ref: '#/components/schemas/Types' - userIds: - $ref: '#/components/schemas/UserIds' - withOutputs: - $ref: '#/components/schemas/WithOutputs' EndpointIds: description: List of endpoint IDs (cannot contain empty strings) items: @@ -638,10 +601,12 @@ components: type: string minItems: 1 type: array - ExecuteActionRequestBody: + ExecuteRouteRequestBody: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -652,6 +617,8 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: parameters: @@ -665,30 +632,39 @@ components: - command required: - parameters - FileDownloadRequestParams: + GetEndpointActionListRouteQuery: type: object properties: - action_id: - type: string - file_id: - type: string - required: - - action_id - - file_id - FileInfoRequestParams: - type: object - properties: - action_id: - type: string - file_id: - type: string - required: - - action_id - - file_id - FileUploadActionRequestBody: + agentIds: + $ref: '#/components/schemas/AgentIds' + agentTypes: + $ref: '#/components/schemas/AgentTypes' + commands: + $ref: '#/components/schemas/Commands' + endDate: + $ref: '#/components/schemas/EndDate' + page: + $ref: '#/components/schemas/Page' + pageSize: + default: 10 + description: Number of items per page + maximum: 10000 + minimum: 1 + type: integer + startDate: + $ref: '#/components/schemas/StartDate' + types: + $ref: '#/components/schemas/Types' + userIds: + $ref: '#/components/schemas/UserIds' + withOutputs: + $ref: '#/components/schemas/WithOutputs' + GetFileRouteRequestBody: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -699,24 +675,29 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: - file: - format: binary - type: string parameters: type: object properties: - overwrite: - default: false - type: boolean + path: + type: string + required: + - path required: - parameters - - file - GetFileActionRequestBody: + GetProcessesRouteRequestBody: + $ref: '#/components/schemas/NoParametersRequestSchema' + IsolateRouteRequestBody: + $ref: '#/components/schemas/NoParametersRequestSchema' + KillOrSuspendActionSchema: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -727,15 +708,22 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: parameters: - type: object - properties: - path: - type: string - required: - - path + oneOf: + - type: object + properties: + pid: + minimum: 1 + type: integer + - type: object + properties: + entity_id: + minLength: 1 + type: string required: - parameters ListRequestQuery: @@ -790,6 +778,28 @@ components: minLength: 1 pattern: ^(?! *$).+$ type: string + NoParametersRequestSchema: + type: object + properties: + body: + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids + required: + - body Page: default: 1 description: Page number @@ -804,45 +814,17 @@ components: Parameters: description: Optional parameters object type: object - ProcessActionSchemas: - allOf: - - type: object - properties: - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' - - type: object - properties: - parameters: - oneOf: - - type: object - properties: - pid: - minimum: 1 - type: integer - - type: object - properties: - entity_id: - minLength: 1 - type: string - required: - - parameters ProtectionUpdatesNoteResponse: type: object properties: note: type: string - ScanActionRequestBody: + ScanRouteRequestBody: allOf: - type: object properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' alert_ids: $ref: '#/components/schemas/AlertIds' case_ids: @@ -853,6 +835,8 @@ components: $ref: '#/components/schemas/EndpointIds' parameters: $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids - type: object properties: parameters: @@ -875,16 +859,52 @@ components: minimum: 1 type: integer Type: + description: Type of response action enum: - automated - manual type: string Types: + description: List of types of response actions items: $ref: '#/components/schemas/Type' maxLength: 2 minLength: 1 type: array + UnisolateRouteRequestBody: + $ref: '#/components/schemas/NoParametersRequestSchema' + UploadRouteRequestBody: + allOf: + - type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids + - type: object + properties: + file: + format: binary + type: string + parameters: + type: object + properties: + overwrite: + default: false + type: boolean + required: + - parameters + - file UserIds: description: User IDs oneOf: @@ -896,7 +916,7 @@ components: - minLength: 1 type: string WithOutputs: - description: With Outputs + description: Shows detailed outputs for an action response oneOf: - items: minLength: 1 diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/data_ingestion_hub_header/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/data_ingestion_hub_header/index.test.tsx new file mode 100644 index 0000000000000..e9a9c068b0926 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/data_ingestion_hub_header/index.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render } from '@testing-library/react'; +import { DataIngestionHubHeader } from '.'; +import darkRocket from '../images/dark_rocket.png'; +import { useCurrentUser } from '../../../../lib/kibana'; + +jest.mock('../../../../lib/kibana', () => ({ + useCurrentUser: jest.fn(), + useEuiTheme: jest.fn(() => ({ euiTheme: { colorTheme: 'DARK' } })), +})); + +const mockUseCurrentUser = useCurrentUser as jest.Mock; + +describe('WelcomeHeaderComponent', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render fullName when fullName is provided', () => { + const fullName = 'John Doe'; + mockUseCurrentUser.mockReturnValue({ fullName }); + const { getByText } = render(<DataIngestionHubHeader />); + const titleElement = getByText(`Hi ${fullName}!`); + expect(titleElement).toBeInTheDocument(); + }); + + it('should render username when fullName is an empty string', () => { + const fullName = ''; + const username = 'jd'; + mockUseCurrentUser.mockReturnValue({ fullName, username }); + + const { getByText } = render(<DataIngestionHubHeader />); + const titleElement = getByText(`Hi ${username}!`); + expect(titleElement).toBeInTheDocument(); + }); + + it('should render username when fullName is not provided', () => { + const username = 'jd'; + mockUseCurrentUser.mockReturnValue({ username }); + + const { getByText } = render(<DataIngestionHubHeader />); + const titleElement = getByText(`Hi ${username}!`); + expect(titleElement).toBeInTheDocument(); + }); + + it('should not render the greeting message if both fullName and username are not available', () => { + mockUseCurrentUser.mockReturnValue({}); + + const { queryByTestId } = render(<DataIngestionHubHeader />); + const greetings = queryByTestId('data-ingestion-hub-header-greetings'); + expect(greetings).not.toBeInTheDocument(); + }); + + it('should render subtitle', () => { + const { getByText } = render(<DataIngestionHubHeader />); + const subtitleElement = getByText('Welcome to Elastic Security'); + expect(subtitleElement).toBeInTheDocument(); + }); + + it('should render description', () => { + const { getByText } = render(<DataIngestionHubHeader />); + const descriptionElement = getByText('Follow these steps to set up your workspace.'); + expect(descriptionElement).toBeInTheDocument(); + }); + + it('should render the rocket dark image when the theme is DARK', () => { + const { queryByTestId } = render(<DataIngestionHubHeader />); + const image = queryByTestId('data-ingestion-hub-header-image'); + expect(image).toHaveStyle({ backgroundImage: `url(${darkRocket})` }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/data_ingestion_hub_header/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/data_ingestion_hub_header/index.tsx index 02d7fb6a26c96..167029c5cc431 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/data_ingestion_hub_header/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/data_ingestion_hub_header/index.tsx @@ -6,9 +6,63 @@ */ import React from 'react'; +import classnames from 'classnames'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { + GET_STARTED_PAGE_TITLE, + GET_STARTED_DATA_INGESTION_HUB_DESCRIPTION, + GET_STARTED_DATA_INGESTION_HUB_SUBTITLE, +} from '../translations'; +import { useCurrentUser } from '../../../../lib/kibana'; +import { useDataIngestionHubHeaderStyles } from '../styles/data_ingestion_hub_header.styles'; const DataIngestionHubHeaderComponent: React.FC = () => { - return <div data-test-subj="data-ingestion-hub-header" />; + const userName = useCurrentUser(); + + const { + headerContentStyles, + headerImageStyles, + headerTitleStyles, + headerSubtitleStyles, + headerDescriptionStyles, + } = useDataIngestionHubHeaderStyles(); + + // Full name could be null, user name should always exist + const name = userName?.fullName || userName?.username; + + const headerSubtitleClassNames = classnames('eui-displayBlock', headerSubtitleStyles); + const headerDescriptionClassNames = classnames('eui-displayBlock', headerDescriptionStyles); + + return ( + <EuiFlexGroup + data-test-subj="data-ingestion-hub-header" + justifyContent="center" + alignItems="center" + > + <EuiFlexItem + data-test-subj="data-ingestion-hub-header-image" + grow={false} + className={headerImageStyles} + /> + <EuiFlexItem grow={false} className={headerContentStyles}> + {name && ( + <EuiTitle + size="l" + className={headerTitleStyles} + data-test-subj="data-ingestion-hub-header-greetings" + > + <span>{GET_STARTED_PAGE_TITLE(name)}</span> + </EuiTitle> + )} + <EuiSpacer size="s" /> + <h1 className={headerSubtitleClassNames}>{GET_STARTED_DATA_INGESTION_HUB_SUBTITLE}</h1> + <EuiSpacer size="s" /> + <span className={headerDescriptionClassNames}> + {GET_STARTED_DATA_INGESTION_HUB_DESCRIPTION} + </span> + </EuiFlexItem> + </EuiFlexGroup> + ); }; export const DataIngestionHubHeader = React.memo(DataIngestionHubHeaderComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.ts index 6bf7ff9cced91..a99cdea4faf28 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/helpers.ts @@ -21,6 +21,8 @@ import { CreateProjectSteps, QuickStartSectionCardsId } from './types'; export const CONTENT_WIDTH = 1150; +export const IMG_HEADER_WIDTH = 128; + export const DEFAULT_FINISHED_STEPS: Partial<Record<CardId, StepId[]>> = { [QuickStartSectionCardsId.createFirstProject]: [CreateProjectSteps.createFirstProject], }; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/dark_rocket.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/dark_rocket.png new file mode 100644 index 0000000000000..a4d3ba73a8a02 Binary files /dev/null and b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/dark_rocket.png differ diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/rocket.png b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/rocket.png new file mode 100644 index 0000000000000..0d899f1c5e201 Binary files /dev/null and b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/images/rocket.png differ diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.tsx index b5ec40b703895..8451cf2178f49 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.tsx +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.tsx @@ -58,7 +58,7 @@ export const OnboardingComponent: React.FC<OnboardingProps> = ({ productTypes?.find((product) => product.product_line === ProductLine.security)?.product_tier, [productTypes] ); - const { wrapperStyles, progressSectionStyles, stepsSectionStyles, bannerStyles } = + const { wrapperStyles, headerSectionStyles, progressSectionStyles, stepsSectionStyles } = useOnboardingStyles(); const { telemetry, storage } = useKibana().services; const onStepLinkClicked = useCallback( @@ -89,14 +89,23 @@ export const OnboardingComponent: React.FC<OnboardingProps> = ({ [isDataIngestionHubEnabled, productTier] ); + const kibanaPageTemplateSectionStyles = useMemo( + () => (isDataIngestionHubEnabled ? headerSectionStyles : ''), + [headerSectionStyles, isDataIngestionHubEnabled] + ); + return ( <div className={wrapperStyles}> {useIsStillYear2024() && showAVCBanner && ( - <KibanaPageTemplate.Section paddingSize="none" className={bannerStyles}> + <KibanaPageTemplate.Section paddingSize="none"> <AVCResultsBanner2024 onDismiss={onBannerDismiss} /> </KibanaPageTemplate.Section> )} - <KibanaPageTemplate.Section restrictWidth={CONTENT_WIDTH} paddingSize="xl"> + <KibanaPageTemplate.Section + className={kibanaPageTemplateSectionStyles} + restrictWidth={CONTENT_WIDTH} + paddingSize="xl" + > {renderDataIngestionHubHeader} </KibanaPageTemplate.Section> <KibanaPageTemplate.Section diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/data_ingestion_hub_header.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/data_ingestion_hub_header.styles.ts new file mode 100644 index 0000000000000..807d02ebec2b3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/data_ingestion_hub_header.styles.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 { useEuiTheme, COLOR_MODES_STANDARD } from '@elastic/eui'; +import { css } from '@emotion/css'; +import { useMemo } from 'react'; +import { CONTENT_WIDTH, IMG_HEADER_WIDTH } from '../helpers'; +import rocket from '../images/rocket.png'; +import darkRocket from '../images/dark_rocket.png'; + +export const useDataIngestionHubHeaderStyles = () => { + const { euiTheme, colorMode } = useEuiTheme(); + + const headerBackgroundImage = useMemo( + () => (colorMode === COLOR_MODES_STANDARD.dark ? darkRocket : rocket), + [colorMode] + ); + + const dataIngestionHubHeaderStyles = useMemo(() => { + return { + headerImageStyles: css({ + backgroundImage: `url(${headerBackgroundImage})`, + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundPositionX: 'center', + backgroundPositionY: 'center', + width: `${IMG_HEADER_WIDTH}px`, + height: `${IMG_HEADER_WIDTH}px`, + }), + headerTitleStyles: css({ + fontSize: `${euiTheme.base}px`, + color: euiTheme.colors.darkShade, + fontWeight: euiTheme.font.weight.bold, + lineHeight: euiTheme.size.l, + }), + headerSubtitleStyles: css({ + fontSize: `${euiTheme.base * 2.125}px`, + color: euiTheme.colors.title, + fontWeight: euiTheme.font.weight.bold, + lineHeight: euiTheme.size.xxl, + }), + headerDescriptionStyles: css({ + fontSize: `${euiTheme.base}px`, + color: euiTheme.colors.subduedText, + lineHeight: euiTheme.size.l, + fontWeight: euiTheme.font.weight.regular, + }), + headerContentStyles: css({ + width: `${CONTENT_WIDTH / 2}px`, + }), + }; + }, [ + euiTheme.base, + euiTheme.colors.darkShade, + euiTheme.colors.subduedText, + euiTheme.colors.title, + euiTheme.font.weight.bold, + euiTheme.font.weight.regular, + euiTheme.size.l, + euiTheme.size.xxl, + headerBackgroundImage, + ]); + return dataIngestionHubHeaderStyles; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/onboarding.styles.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/onboarding.styles.ts index 4f2eb59f06bf2..a16b736dd7efd 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/onboarding.styles.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/styles/onboarding.styles.ts @@ -15,7 +15,10 @@ export const useOnboardingStyles = () => { return useMemo( () => ({ wrapperStyles: css({ - margin: `0 -${euiTheme.size.l}`, + margin: `-${euiTheme.size.l} -${euiTheme.size.l}`, + }), + headerSectionStyles: css({ + backgroundColor: euiTheme.colors.lightestShade, }), progressSectionStyles: css({ backgroundColor: euiTheme.colors.lightestShade, @@ -25,9 +28,6 @@ export const useOnboardingStyles = () => { padding: `0 ${euiTheme.size.xxl} ${euiTheme.size.xxxl}`, backgroundColor: euiTheme.colors.lightestShade, }), - bannerStyles: css({ - margin: `-${euiTheme.size.l} 0`, - }), }), [ euiTheme.colors.lightestShade, diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/translations.ts b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/translations.ts index bad766fc41a4d..27471327b72fe 100644 --- a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/translations.ts +++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/translations.ts @@ -27,6 +27,20 @@ export const GET_STARTED_PAGE_DESCRIPTION = i18n.translate( } ); +export const GET_STARTED_DATA_INGESTION_HUB_SUBTITLE = i18n.translate( + 'xpack.securitySolution.onboarding.subTitle', + { + defaultMessage: `Welcome to Elastic Security`, + } +); + +export const GET_STARTED_DATA_INGESTION_HUB_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.onboarding.description', + { + defaultMessage: `Follow these steps to set up your workspace.`, + } +); + export const CURRENT_PLAN_LABEL = i18n.translate( 'xpack.securitySolution.onboarding.currentPlan.label', { diff --git a/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/index.ts b/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/index.ts index dc119de848dec..678c0d63e4be6 100644 --- a/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/index.ts @@ -6,9 +6,10 @@ */ import type { - HostIsolationRequestBody, - ResponseActionApiResponse, -} from '../../../../../common/endpoint/types'; + IsolationRouteRequestBody, + UnisolationRouteRequestBody, +} from '../../../../../common/api/endpoint'; +import type { ResponseActionApiResponse } from '../../../../../common/endpoint/types'; import { KibanaServices } from '../../kibana'; import { ISOLATE_HOST_ROUTE_V2, @@ -19,7 +20,7 @@ import { /** Isolates a Host running either elastic endpoint or fleet agent */ export const isolateHost = async ( - params: HostIsolationRequestBody + params: IsolationRouteRequestBody ): Promise<ResponseActionApiResponse> => { return KibanaServices.get().http.post<ResponseActionApiResponse>(ISOLATE_HOST_ROUTE_V2, { body: JSON.stringify(params), @@ -29,7 +30,7 @@ export const isolateHost = async ( /** Un-isolates a Host running either elastic endpoint or fleet agent */ export const unIsolateHost = async ( - params: HostIsolationRequestBody + params: UnisolationRouteRequestBody ): Promise<ResponseActionApiResponse> => { return KibanaServices.get().http.post<ResponseActionApiResponse>(UNISOLATE_HOST_ROUTE_V2, { body: JSON.stringify(params), diff --git a/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/mocks.ts b/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/mocks.ts index 4881fc3f1569f..bd65a1de60612 100644 --- a/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/mocks.ts +++ b/x-pack/plugins/security_solution/public/common/lib/endpoint/endpoint_isolation/mocks.ts @@ -5,10 +5,8 @@ * 2.0. */ -import type { - HostIsolationRequestBody, - HostIsolationResponse, -} from '../../../../../common/endpoint/types'; +import type { IsolationRouteRequestBody } from '../../../../../common/api/endpoint'; +import type { HostIsolationResponse } from '../../../../../common/endpoint/types'; import type { ResponseProvidersInterface } from '../../../mock/endpoint/http_handler_mock_factory'; import { httpHandlerMockFactory } from '../../../mock/endpoint/http_handler_mock_factory'; import { @@ -16,7 +14,7 @@ import { UNISOLATE_HOST_ROUTE_V2, } from '../../../../../common/endpoint/constants'; -export const hostIsolationRequestBodyMock = (): HostIsolationRequestBody => { +export const hostIsolationRequestBodyMock = (): IsolationRouteRequestBody => { return { endpoint_ids: ['88c04a90-b19c-11eb-b838-222'], alert_ids: ['88c04a90-b19c-11eb-b838-333'], diff --git a/x-pack/plugins/security_solution/public/common/lib/process_actions/index.ts b/x-pack/plugins/security_solution/public/common/lib/process_actions/index.ts index b8cb7c04f469c..960ec21f41ef4 100644 --- a/x-pack/plugins/security_solution/public/common/lib/process_actions/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/process_actions/index.ts @@ -6,10 +6,10 @@ */ import type { - ResponseActionApiResponse, KillProcessRequestBody, SuspendProcessRequestBody, -} from '../../../../common/endpoint/types'; +} from '../../../../common/api/endpoint'; +import type { ResponseActionApiResponse } from '../../../../common/endpoint/types'; import { KibanaServices } from '../kibana'; import { KILL_PROCESS_ROUTE, SUSPEND_PROCESS_ROUTE } from '../../../../common/endpoint/constants'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts index 07f14830d6a71..2fdd8cf8120d7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.test.ts @@ -5,82 +5,117 @@ * 2.0. */ +import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; import { parseEsqlQuery, computeHasMetadataOperator } from './esql_validator'; -import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; +import { isAggregatingQuery } from '@kbn/securitysolution-utils'; -jest.mock('@kbn/securitysolution-utils', () => ({ computeIsESQLQueryAggregating: jest.fn() })); +jest.mock('@kbn/securitysolution-utils', () => ({ isAggregatingQuery: jest.fn() })); -const computeIsESQLQueryAggregatingMock = computeIsESQLQueryAggregating as jest.Mock; +const isAggregatingQueryMock = isAggregatingQuery as jest.Mock; + +const getQeryAst = (query: string) => { + const { ast } = getAstAndSyntaxErrors(query); + return ast; +}; describe('computeHasMetadataOperator', () => { it('should be false if query does not have operator', () => { - expect(computeHasMetadataOperator('from test*')).toBe(false); - expect(computeHasMetadataOperator('from test* [metadata]')).toBe(false); - expect(computeHasMetadataOperator('from test* [metadata id]')).toBe(false); - expect(computeHasMetadataOperator('from metadata*')).toBe(false); - expect(computeHasMetadataOperator('from test* | keep metadata')).toBe(false); - expect(computeHasMetadataOperator('from test* | eval x="[metadata _id]"')).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from test*'))).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from test* [metadata]'))).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from test* [metadata id]'))).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from metadata*'))).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from test* | keep metadata'))).toBe(false); + expect(computeHasMetadataOperator(getQeryAst('from test* | eval x="[metadata _id]"'))).toBe( + false + ); }); it('should be true if query has operator', () => { - expect(computeHasMetadataOperator('from test* metadata _id')).toBe(true); - expect(computeHasMetadataOperator('from test* metadata _id, _index')).toBe(true); - expect(computeHasMetadataOperator('from test* metadata _index, _id')).toBe(true); - expect(computeHasMetadataOperator('from test* metadata _id ')).toBe(true); - expect(computeHasMetadataOperator('from test* metadata _id | limit 10')).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id, _index'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _index, _id'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id '))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* metadata _id | limit 10'))).toBe( + true + ); expect( - computeHasMetadataOperator(`from packetbeat* metadata + computeHasMetadataOperator( + getQeryAst(`from packetbeat* metadata _id | limit 100`) + ) ).toBe(true); // still validates deprecated square bracket syntax - expect(computeHasMetadataOperator('from test* [metadata _id]')).toBe(true); - expect(computeHasMetadataOperator('from test* [metadata _id, _index]')).toBe(true); - expect(computeHasMetadataOperator('from test* [metadata _index, _id]')).toBe(true); - expect(computeHasMetadataOperator('from test* [ metadata _id ]')).toBe(true); - expect(computeHasMetadataOperator('from test* [ metadata _id] ')).toBe(true); - expect(computeHasMetadataOperator('from test* [ metadata _id] | limit 10')).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* [metadata _id]'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* [metadata _id, _index]'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* [metadata _index, _id]'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* [ metadata _id ]'))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* [ metadata _id] '))).toBe(true); + expect(computeHasMetadataOperator(getQeryAst('from test* [ metadata _id] | limit 10'))).toBe( + true + ); expect( - computeHasMetadataOperator(`from packetbeat* [metadata + computeHasMetadataOperator( + getQeryAst(`from packetbeat* [metadata _id ] | limit 100`) + ) ).toBe(true); }); }); describe('parseEsqlQuery', () => { it('returns isMissingMetadataOperator true when query is not aggregating and does not have metadata operator', () => { - computeIsESQLQueryAggregatingMock.mockReturnValueOnce(false); + isAggregatingQueryMock.mockReturnValueOnce(false); expect(parseEsqlQuery('from test*')).toEqual({ + errors: [], isEsqlQueryAggregating: false, isMissingMetadataOperator: true, }); }); it('returns isMissingMetadataOperator false when query is not aggregating and has metadata operator', () => { - computeIsESQLQueryAggregatingMock.mockReturnValueOnce(false); + isAggregatingQueryMock.mockReturnValueOnce(false); expect(parseEsqlQuery('from test* metadata _id')).toEqual({ + errors: [], isEsqlQueryAggregating: false, isMissingMetadataOperator: false, }); }); it('returns isMissingMetadataOperator false when query is aggregating', () => { - computeIsESQLQueryAggregatingMock.mockReturnValue(true); + isAggregatingQueryMock.mockReturnValue(true); expect(parseEsqlQuery('from test*')).toEqual({ + errors: [], isEsqlQueryAggregating: true, isMissingMetadataOperator: false, }); expect(parseEsqlQuery('from test* metadata _id')).toEqual({ + errors: [], isEsqlQueryAggregating: true, isMissingMetadataOperator: false, }); }); + + it('returns error when query is syntactically invalid', () => { + isAggregatingQueryMock.mockReturnValueOnce(false); + + expect(parseEsqlQuery('aaa bbbb ssdasd')).toEqual({ + errors: expect.arrayContaining([ + expect.objectContaining({ + message: + "SyntaxError: mismatched input 'aaa' expecting {'explain', 'from', 'meta', 'metrics', 'row', 'show'}", + }), + ]), + isEsqlQueryAggregating: false, + isMissingMetadataOperator: true, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts index 484f78c53f0e0..869e379c21aed 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/logic/esql_validator.ts @@ -7,8 +7,11 @@ import { isEmpty } from 'lodash'; import type { QueryClient } from '@tanstack/react-query'; -import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; +import { isAggregatingQuery } from '@kbn/securitysolution-utils'; +import type { ESQLAst } from '@kbn/esql-ast'; +import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; +import { isColumnItem, isOptionItem } from '@kbn/esql-validation-autocomplete'; import { KibanaServices } from '../../../common/lib/kibana'; import type { ValidationError, ValidationFunc } from '../../../shared_imports'; @@ -21,6 +24,7 @@ export type FieldType = 'string'; export enum ERROR_CODES { INVALID_ESQL = 'ERR_INVALID_ESQL', + INVALID_SYNTAX = 'ERR_INVALID_SYNTAX', ERR_MISSING_ID_FIELD_FROM_RESULT = 'ERR_MISSING_ID_FIELD_FROM_RESULT', } @@ -34,11 +38,52 @@ const constructValidationError = (error: Error) => { }; }; +const constructSyntaxError = (error: Error) => { + return { + code: ERROR_CODES.INVALID_SYNTAX, + message: error?.message + ? i18n.esqlValidationErrorMessage(error.message) + : i18n.ESQL_VALIDATION_UNKNOWN_ERROR, + error, + }; +}; + +const getMetadataOption = (ast: ESQLAst) => { + const fromCommand = ast.find((astItem) => astItem.type === 'command' && astItem.name === 'from'); + + if (!fromCommand?.args) { + return undefined; + } + + // Check whether the `from` command has `metadata` operator + for (const fromArg of fromCommand.args) { + if (isOptionItem(fromArg) && fromArg.name === 'metadata') { + return fromArg; + } + } + + return undefined; +}; + /** * checks whether query has metadata _id operator */ -export const computeHasMetadataOperator = (esqlQuery: string) => { - return /(?<!\|[\s\S.]*)\s*metadata[\s\S.]*_id[\s\S.]*/i.test(esqlQuery?.split('|')?.[0]); +export const computeHasMetadataOperator = (ast: ESQLAst) => { + // Check whether the `from` command has `metadata` operator + const metadataOption = getMetadataOption(ast); + if (!metadataOption) { + return false; + } + + // Check whether the `metadata` operator has `_id` argument + const idColumnItem = metadataOption.args.find( + (fromArg) => isColumnItem(fromArg) && fromArg.name === '_id' + ); + if (!idColumnItem) { + return false; + } + + return true; }; /** @@ -61,7 +106,12 @@ export const esqlValidator = async ( const queryClient = (customData.value as { queryClient: QueryClient | undefined })?.queryClient; const services = KibanaServices.get(); - const { isEsqlQueryAggregating, isMissingMetadataOperator } = parseEsqlQuery(query); + const { isEsqlQueryAggregating, isMissingMetadataOperator, errors } = parseEsqlQuery(query); + + // Check if there are any syntax errors + if (errors.length) { + return constructSyntaxError(new Error(errors[0].message)); + } if (isMissingMetadataOperator) { return { @@ -97,11 +147,14 @@ export const esqlValidator = async ( * - if it's non aggregation query it must have metadata operator */ export const parseEsqlQuery = (query: string) => { - const isEsqlQueryAggregating = computeIsESQLQueryAggregating(query); + const { ast, errors } = getAstAndSyntaxErrors(query); + + const isEsqlQueryAggregating = isAggregatingQuery(ast); return { + errors, isEsqlQueryAggregating, // non-aggregating query which does not have [metadata], is not a valid one - isMissingMetadataOperator: !isEsqlQueryAggregating && !computeHasMetadataOperator(query), + isMissingMetadataOperator: !isEsqlQueryAggregating && !computeHasMetadataOperator(ast), }; }; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.test.ts index 996b3ca044864..1a13f0ff8e3a2 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.test.ts @@ -12,11 +12,9 @@ import { getESQLQueryColumns } from '@kbn/esql-utils'; import { useAllEsqlRuleFields } from './use_all_esql_rule_fields'; import { createQueryWrapperMock } from '../../../common/__mocks__/query_wrapper'; -import { parseEsqlQuery } from '../../rule_creation/logic/esql_validator'; +import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; -jest.mock('../../rule_creation/logic/esql_validator', () => ({ - parseEsqlQuery: jest.fn(), -})); +jest.mock('@kbn/securitysolution-utils', () => ({ computeIsESQLQueryAggregating: jest.fn() })); jest.mock('@kbn/esql-utils', () => { return { @@ -25,7 +23,7 @@ jest.mock('@kbn/esql-utils', () => { }; }); -const parseEsqlQueryMock = parseEsqlQuery as jest.Mock; +const computeIsESQLQueryAggregatingMock = computeIsESQLQueryAggregating as jest.Mock; const getESQLQueryColumnsMock = getESQLQueryColumns as jest.Mock; const { wrapper } = createQueryWrapperMock(); @@ -47,7 +45,8 @@ const mockEsqlDatatable = { columns: [{ id: '_custom_field', name: '_custom_field', meta: { type: 'string' } }], }; -describe('useAllEsqlRuleFields', () => { +// FLAKY: https://github.com/elastic/kibana/issues/190063 +describe.skip('useAllEsqlRuleFields', () => { beforeEach(() => { jest.clearAllMocks(); getESQLQueryColumnsMock.mockImplementation(({ esqlQuery }) => @@ -60,7 +59,7 @@ describe('useAllEsqlRuleFields', () => { : mockEsqlDatatable.columns ) ); - parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: false }); + computeIsESQLQueryAggregatingMock.mockReturnValue(false); }); it('should return loading true when esql fields still loading', () => { @@ -103,7 +102,7 @@ describe('useAllEsqlRuleFields', () => { }); it('should return index pattern fields concatenated with ES|QL fields when ES|QL query is non-aggregating', async () => { - parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: false }); + computeIsESQLQueryAggregatingMock.mockReturnValue(false); const { result, waitFor } = renderHook( () => @@ -126,7 +125,7 @@ describe('useAllEsqlRuleFields', () => { }); it('should return only ES|QL fields when ES|QL query is aggregating', async () => { - parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: true }); + computeIsESQLQueryAggregatingMock.mockReturnValue(true); const { result, waitFor } = renderHook( () => @@ -148,7 +147,7 @@ describe('useAllEsqlRuleFields', () => { it('should deduplicate index pattern fields and ES|QL fields when fields have same name', async () => { // getESQLQueryColumnsMock.mockClear(); - parseEsqlQueryMock.mockReturnValue({ isEsqlQueryAggregating: false }); + computeIsESQLQueryAggregatingMock.mockReturnValue(false); const { result, waitFor } = renderHook( () => diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.ts index a67b990c88b80..80bfc364e5b51 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_all_esql_rule_fields.ts @@ -13,7 +13,7 @@ import useDebounce from 'react-use/lib/useDebounce'; import { useQuery } from '@tanstack/react-query'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { parseEsqlQuery } from '../../rule_creation/logic/esql_validator'; +import { computeIsESQLQueryAggregating } from '@kbn/securitysolution-utils'; import { getEsqlQueryConfig } from '../../rule_creation/logic/get_esql_query_config'; @@ -89,8 +89,8 @@ export const useAllEsqlRuleFields: UseAllEsqlRuleFields = ({ esqlQuery, indexPat const [debouncedEsqlQuery, setDebouncedEsqlQuery] = useState<string | undefined>(undefined); const { fields: esqlFields, isLoading } = useEsqlFields(debouncedEsqlQuery); - const { isEsqlQueryAggregating } = useMemo( - () => parseEsqlQuery(debouncedEsqlQuery ?? ''), + const isEsqlQueryAggregating = useMemo( + () => computeIsESQLQueryAggregating(debouncedEsqlQuery ?? ''), [debouncedEsqlQuery] ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/helpers.test.ts new file mode 100644 index 0000000000000..d8a1823622bdf --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/helpers.test.ts @@ -0,0 +1,472 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; +import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { getRulesSchemaMock } from '../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock'; +import { isSubmitDisabled, prepareNewItemsForSubmission, prepareToCloseAlerts } from './helpers'; +import type { Rule } from '../../../rule_management/logic/types'; +import type { AlertData } from '../../utils/types'; + +const items = [ + { + ...getExceptionListItemSchemaMock(), + }, +]; + +const alertDataMock: AlertData = { + '@timestamp': '1234567890', + _id: 'test-id', + file: { path: 'test/path' }, +}; + +describe('add_exception_flyout#helpers', () => { + describe('isSubmitDisabled', () => { + it('returns true if "isSubmitting" is "true"', () => { + expect( + isSubmitDisabled({ + isSubmitting: true, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if "isClosingAlerts" is "true"', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: true, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if "itemConditionValidationErrorExists" is "true"', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: true, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if "commentErrorExists" is "true"', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: true, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if "expireErrorExists" is "true"', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: true, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if item name is empty', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: ' ', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.DETECTION, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if error submitting exists', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: new Error('uh oh'), + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.DETECTION, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if all items do not include any entries', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: [ + { + ...getExceptionListItemSchemaMock(), + entries: [], + }, + ], + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.DETECTION, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeTruthy(); + }); + + it('returns true if exception is to be added to a list, but no list is specified', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.DETECTION, + exceptionListsToAddTo: [], + }) + ).toBeTruthy(); + }); + + it('returns true if exception is to be added to a rule but no rule is specified', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'select_rules_to_add_to', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + exceptionListsToAddTo: [], + }) + ).toBeTruthy(); + }); + + it('returns false if conditions are met for adding exception to a rule', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'select_rules_to_add_to', + selectedRulesToAddTo: [ + { + ...getRulesSchemaMock(), + exceptions_list: [], + } as Rule, + ], + listType: ExceptionListTypeEnum.RULE_DEFAULT, + exceptionListsToAddTo: [], + }) + ).toBeFalsy(); + }); + + it('returns false if conditions are met for adding exception to a list', () => { + expect( + isSubmitDisabled({ + isSubmitting: false, + isClosingAlerts: false, + errorSubmitting: null, + exceptionItemName: 'Item name', + exceptionItems: items, + itemConditionValidationErrorExists: false, + commentErrorExists: false, + expireErrorExists: false, + addExceptionToRadioSelection: 'add_to_lists', + selectedRulesToAddTo: [], + listType: ExceptionListTypeEnum.DETECTION, + exceptionListsToAddTo: [getExceptionListSchemaMock()], + }) + ).toBeFalsy(); + }); + }); + + // Doesn't explicitly test "enrichNewExceptionItems" used within helper as that function + // is covered with unit tests itself. + describe('prepareNewItemsForSubmission', () => { + it('returns "addToLists" true and the sharedListToAddTo lists to add to if correct radio selection and lists are referenced', () => { + const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({ + sharedListToAddTo: [getExceptionListSchemaMock()], + addExceptionToRadioSelection: 'add_to_lists', + exceptionListsToAddTo: [], + exceptionItemName: 'Test item', + newComment: '', + listType: ExceptionListTypeEnum.DETECTION, + osTypesSelection: [], + expireTime: undefined, + exceptionItems: [], + }); + + expect(addToLists).toBeTruthy(); + expect(addToRules).toBeFalsy(); + expect(listsToAddTo).toEqual([getExceptionListSchemaMock()]); + }); + + it('returns "addToLists" true and the exceptionListsToAddTo if correct radio selection and lists are referenced', () => { + const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({ + sharedListToAddTo: [], + addExceptionToRadioSelection: 'add_to_lists', + exceptionListsToAddTo: [getExceptionListSchemaMock()], + exceptionItemName: 'Test item', + newComment: '', + listType: ExceptionListTypeEnum.DETECTION, + osTypesSelection: [], + expireTime: undefined, + exceptionItems: [], + }); + + expect(addToLists).toBeTruthy(); + expect(addToRules).toBeFalsy(); + expect(listsToAddTo).toEqual([getExceptionListSchemaMock()]); + }); + + it('returns "addToLists" false if no exception lists are specified as the lists to add to', () => { + const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({ + sharedListToAddTo: [], + addExceptionToRadioSelection: 'add_to_lists', + exceptionListsToAddTo: [], + exceptionItemName: 'Test item', + newComment: '', + listType: ExceptionListTypeEnum.DETECTION, + osTypesSelection: [], + expireTime: undefined, + exceptionItems: [], + }); + + expect(addToLists).toBeFalsy(); + expect(addToRules).toBeFalsy(); + expect(listsToAddTo).toEqual([]); + }); + + it('returns "addToRules" true if radio selection is "add_to_rule"', () => { + const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({ + sharedListToAddTo: [], + addExceptionToRadioSelection: 'add_to_rule', + exceptionListsToAddTo: [], + exceptionItemName: 'Test item', + newComment: '', + listType: ExceptionListTypeEnum.DETECTION, + osTypesSelection: [], + expireTime: undefined, + exceptionItems: [], + }); + + expect(addToLists).toBeFalsy(); + expect(addToRules).toBeTruthy(); + expect(listsToAddTo).toEqual([]); + }); + + it('returns "addToRules" true if radio selection is "add_to_rules"', () => { + const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({ + sharedListToAddTo: [], + addExceptionToRadioSelection: 'add_to_rules', + exceptionListsToAddTo: [], + exceptionItemName: 'Test item', + newComment: '', + listType: ExceptionListTypeEnum.DETECTION, + osTypesSelection: [], + expireTime: undefined, + exceptionItems: [], + }); + + expect(addToLists).toBeFalsy(); + expect(addToRules).toBeTruthy(); + expect(listsToAddTo).toEqual([]); + }); + + it('returns "addToRules" true if radio selection is "select_rules_to_add_to"', () => { + const { addToRules, addToLists, listsToAddTo } = prepareNewItemsForSubmission({ + sharedListToAddTo: [], + addExceptionToRadioSelection: 'select_rules_to_add_to', + exceptionListsToAddTo: [], + exceptionItemName: 'Test item', + newComment: '', + listType: ExceptionListTypeEnum.DETECTION, + osTypesSelection: [], + expireTime: undefined, + exceptionItems: [], + }); + + expect(addToLists).toBeFalsy(); + expect(addToRules).toBeTruthy(); + expect(listsToAddTo).toEqual([]); + }); + }); + + describe('prepareToCloseAlerts', () => { + it('returns "shouldCloseAlerts" false if no rule ids defined', () => { + const { shouldCloseAlerts, ruleStaticIds } = prepareToCloseAlerts({ + alertData: undefined, + closeSingleAlert: false, + addToRules: true, + rules: [], + bulkCloseAlerts: true, + selectedRulesToAddTo: [], + }); + + expect(shouldCloseAlerts).toBeFalsy(); + expect(ruleStaticIds).toEqual([]); + }); + + it('returns "shouldCloseAlerts" false if neither closeSingleAlert or bulkCloseAlerts are true', () => { + const { shouldCloseAlerts, ruleStaticIds } = prepareToCloseAlerts({ + alertData: undefined, + closeSingleAlert: false, + addToRules: true, + rules: [], + bulkCloseAlerts: false, + selectedRulesToAddTo: [ + { + ...getRulesSchemaMock(), + exceptions_list: [], + } as Rule, + ], + }); + + expect(shouldCloseAlerts).toBeFalsy(); + expect(ruleStaticIds).toEqual(['query-rule-id']); + }); + + it('returns "alertIdToClose" if "alertData" defined and "closeSingleAlert" selected', () => { + const { alertIdToClose, ruleStaticIds } = prepareToCloseAlerts({ + alertData: alertDataMock, + closeSingleAlert: true, + addToRules: true, + rules: [], + bulkCloseAlerts: false, + selectedRulesToAddTo: [ + { + ...getRulesSchemaMock(), + exceptions_list: [], + } as Rule, + ], + }); + + expect(alertIdToClose).toEqual('test-id'); + expect(ruleStaticIds).toEqual(['query-rule-id']); + }); + + it('returns "alertIdToClose" of undefined if "alertData" defined but "closeSingleAlert" is not selected', () => { + const { alertIdToClose, ruleStaticIds } = prepareToCloseAlerts({ + alertData: alertDataMock, + closeSingleAlert: false, + addToRules: true, + rules: [], + bulkCloseAlerts: false, + selectedRulesToAddTo: [ + { + ...getRulesSchemaMock(), + exceptions_list: [], + } as Rule, + ], + }); + + expect(alertIdToClose).toBeUndefined(); + expect(ruleStaticIds).toEqual(['query-rule-id']); + }); + + it('returns rule ids from "rules" if "addToRules" is false', () => { + const { ruleStaticIds } = prepareToCloseAlerts({ + alertData: alertDataMock, + closeSingleAlert: false, + addToRules: false, + rules: [ + { + ...getRulesSchemaMock(), + exceptions_list: [], + } as Rule, + ], + bulkCloseAlerts: false, + selectedRulesToAddTo: [], + }); + + expect(ruleStaticIds).toEqual(['query-rule-id']); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/helpers.ts new file mode 100644 index 0000000000000..23d20f699780d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/helpers.ts @@ -0,0 +1,185 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { Moment } from 'moment'; +import type { ExceptionListSchema, OsTypeArray } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { isEmpty } from 'lodash/fp'; +import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution-list-utils'; + +import type { Rule } from '../../../rule_management/logic/types'; +import { enrichNewExceptionItems } from '../flyout_components/utils'; +import type { AlertData } from '../../utils/types'; + +const RULE_DEFAULT_OPTIONS = ['add_to_rule', 'add_to_rules', 'select_rules_to_add_to']; + +/** + * Determines whether add exception flyout submit button + * should be disabled. + * @param isSubmitting Is submition completed + * @param isClosingAlerts Waiting on close alerts actions to complete + * @param errorSubmitting Any submission errors + * @param exceptionItemName Item name + * @param exceptionItems Items to be created + * @param itemConditionValidationErrorExists Item conditions are invalid + * @param commentErrorExists Comment invalid or errors exist + * @param expireErrorExists Expire time invalid or error exists + * @param addExceptionToRadioSelection Radio selection value denoting whether to add item to lists or rules + * @param selectedRulesToAddTo List of rules item/s should be added to + * @param listType list type of the item being added + * @param exceptionListsToAddTo User selected exception lists to add item to + */ +export const isSubmitDisabled = ({ + isSubmitting, + isClosingAlerts, + errorSubmitting, + exceptionItemName, + exceptionItems, + itemConditionValidationErrorExists, + commentErrorExists, + expireErrorExists, + addExceptionToRadioSelection, + selectedRulesToAddTo, + listType, + exceptionListsToAddTo, +}: { + isSubmitting: boolean; + isClosingAlerts: boolean; + errorSubmitting: Error | null; + exceptionItemName: string; + exceptionItems: ExceptionsBuilderReturnExceptionItem[]; + itemConditionValidationErrorExists: boolean; + commentErrorExists: boolean; + expireErrorExists: boolean; + addExceptionToRadioSelection: string; + selectedRulesToAddTo: Rule[]; + listType: ExceptionListTypeEnum; + exceptionListsToAddTo: ExceptionListSchema[]; +}): boolean => { + return ( + isSubmitting || + isClosingAlerts || + errorSubmitting != null || + exceptionItemName.trim() === '' || + exceptionItems.every((item) => item.entries.length === 0) || + itemConditionValidationErrorExists || + commentErrorExists || + expireErrorExists || + (addExceptionToRadioSelection === 'add_to_lists' && isEmpty(exceptionListsToAddTo)) || + (addExceptionToRadioSelection === 'select_rules_to_add_to' && + isEmpty(selectedRulesToAddTo) && + listType === ExceptionListTypeEnum.RULE_DEFAULT) + ); +}; + +/** + * Helper method for determining if user has selected to add exception + * items to specific rules or to exception lists. It also returns the + * exception items enriched with various flyout values. + * @param sharedListToAddTo Exception list passed into add exception item flyout component + * @param addExceptionToRadioSelection Radio selection value denoting whether to add item to lists or rules + * @param exceptionListsToAddTo User selected exception lists to add item to + * @param exceptionItemName Item name + * @param newComment User added comment + * @param listType list type of the item being added + * @param osTypesSelection For endpoint exceptions, OS selected + * @param expireTime User defined item expire time + * @param exceptionItems Items to be added + */ +export const prepareNewItemsForSubmission = ({ + sharedListToAddTo, + addExceptionToRadioSelection, + exceptionListsToAddTo, + exceptionItemName, + newComment, + listType, + osTypesSelection, + expireTime, + exceptionItems, +}: { + sharedListToAddTo: ExceptionListSchema[] | undefined; + addExceptionToRadioSelection: string; + exceptionListsToAddTo: ExceptionListSchema[]; + exceptionItemName: string; + newComment: string; + listType: ExceptionListTypeEnum; + osTypesSelection: OsTypeArray; + expireTime: Moment | undefined; + exceptionItems: ExceptionsBuilderReturnExceptionItem[]; +}): { + listsToAddTo: ExceptionListSchema[]; + addToLists: boolean; + addToRules: boolean; + items: ExceptionsBuilderReturnExceptionItem[]; +} => { + const addToRules = RULE_DEFAULT_OPTIONS.includes(addExceptionToRadioSelection); + const addToLists = + !!sharedListToAddTo?.length || + (addExceptionToRadioSelection === 'add_to_lists' && !isEmpty(exceptionListsToAddTo)); + const listsToAddTo = sharedListToAddTo?.length ? sharedListToAddTo : exceptionListsToAddTo; + + const items = enrichNewExceptionItems({ + itemName: exceptionItemName, + commentToAdd: newComment, + addToRules, + addToSharedLists: addToLists, + sharedLists: listsToAddTo, + listType, + selectedOs: osTypesSelection, + expireTime, + items: exceptionItems, + }); + + return { + listsToAddTo, + addToLists, + addToRules, + items, + }; +}; + +/** + * Determine whether to close a single alert or bulk close + * alerts. Depending on the selection, need to know alert id + * and rule ids. + * @param alertData Alert the item is being added to + * @param closeSingleAlert User selected to close a single alert + * @param addToRules User selected to add item to 'x' rules + * @param rules Rules to determine which alerts to target when bulk closing + * @param bulkCloseAlerts User selected to close all alerts matching new exception + * @param selectedRulesToAddTo User selected rules to add item to + */ +export const prepareToCloseAlerts = ({ + alertData, + closeSingleAlert, + addToRules, + rules, + bulkCloseAlerts, + selectedRulesToAddTo, +}: { + alertData: AlertData | undefined; + closeSingleAlert: boolean; + addToRules: boolean; + rules: Rule[] | null; + bulkCloseAlerts: boolean; + selectedRulesToAddTo: Rule[]; +}): { + shouldCloseAlerts: boolean; + alertIdToClose: string | undefined; + ruleStaticIds: string[]; +} => { + const alertIdToClose = closeSingleAlert && alertData ? alertData._id : undefined; + const ruleStaticIds = addToRules + ? selectedRulesToAddTo.map(({ rule_id: ruleId }) => ruleId) + : (rules ?? []).map(({ rule_id: ruleId }) => ruleId); + + return { + shouldCloseAlerts: !isEmpty(ruleStaticIds) && (bulkCloseAlerts || closeSingleAlert), + alertIdToClose, + ruleStaticIds, + }; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx index 1f8251c510481..0d2e937931e21 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.test.tsx @@ -6,14 +6,12 @@ */ import React from 'react'; -import type { ReactWrapper } from 'enzyme'; +import type { ReactWrapper, ShallowWrapper } from 'enzyme'; import { mount, shallow } from 'enzyme'; -import { act, fireEvent, render, waitFor } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; -import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; import type { EntriesArray, EntryMatch } from '@kbn/securitysolution-io-ts-list-types'; -import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import { createStubIndexPattern, stubIndexPattern } from '@kbn/data-plugin/common/stubs'; @@ -24,7 +22,6 @@ import { useFetchIndexPatterns } from '../../logic/use_exception_flyout_data'; import { useSignalIndex } from '../../../../detections/containers/detection_engine/alerts/use_signal_index'; import * as helpers from '../../utils/helpers'; import type { Rule } from '../../../rule_management/logic/types'; -import * as i18n from './translations'; import { TestProviders } from '../../../../common/mock'; @@ -33,23 +30,13 @@ import { getRulesSchemaMock, } from '../../../../../common/api/detection_engine/model/rule_schema/mocks'; import type { AlertData } from '../../utils/types'; -import { useFindRules } from '../../../rule_management/logic/use_find_rules'; -import { useFindExceptionListReferences } from '../../logic/use_find_references'; -import { MAX_COMMENT_LENGTH } from '../../../../../common/constants'; jest.mock('../../../../detections/containers/detection_engine/alerts/use_signal_index'); jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../common/containers/source'); jest.mock('../../logic/use_create_update_exception'); jest.mock('../../logic/use_exception_flyout_data'); -jest.mock('../../logic/use_find_references'); -jest.mock('@kbn/securitysolution-hook-utils', () => ({ - ...jest.requireActual('@kbn/securitysolution-hook-utils'), - useAsync: jest.fn(), -})); -jest.mock('../../../rule_management/logic/use_rule'); jest.mock('@kbn/lists-plugin/public'); -jest.mock('../../../rule_management/logic/use_find_rules'); jest.mock('../../../rule_management/api/hooks/use_fetch_rule_by_id_query'); const mockGetExceptionBuilderComponentLazy = getExceptionBuilderComponentLazy as jest.Mock< @@ -63,8 +50,6 @@ const mockFetchIndexPatterns = useFetchIndexPatterns as jest.Mock< >; const mockUseSignalIndex = useSignalIndex as jest.Mock<Partial<ReturnType<typeof useSignalIndex>>>; const mockUseFetchIndex = useFetchIndex as jest.Mock; -const mockUseFindRules = useFindRules as jest.Mock; -const mockUseFindExceptionListReferences = useFindExceptionListReferences as jest.Mock; const alertDataMock: AlertData = { '@timestamp': '1234567890', @@ -83,64 +68,11 @@ describe('When the add exception modal is opened', () => { defaultEndpointItems = jest.spyOn(helpers, 'defaultEndpointExceptionItems'); mockUseAddOrUpdateException.mockImplementation(() => [false, jest.fn()]); - mockFetchIndexPatterns.mockImplementation(() => ({ - isLoading: false, - indexPatterns: stubIndexPattern, - getExtendedFields: () => Promise.resolve([]), - })); mockUseSignalIndex.mockImplementation(() => ({ loading: false, signalIndexName: 'mock-siem-signals-index', })); - mockUseFetchIndex.mockImplementation(() => [ - false, - { - indexPatterns: stubIndexPattern, - }, - ]); - mockUseFindRules.mockImplementation(() => ({ - data: { - rules: [ - { - ...getRulesSchemaMock(), - exceptions_list: [], - } as Rule, - ], - total: 1, - }, - isFetched: true, - })); - mockUseFindExceptionListReferences.mockImplementation(() => [ - false, - false, - { - my_list_id: { - ...getExceptionListSchemaMock(), - id: '123', - list_id: 'my_list_id', - namespace_type: 'single', - type: ExceptionListTypeEnum.DETECTION, - name: 'My exception list', - referenced_rules: [ - { - id: '345', - name: 'My rule', - rule_id: 'my_rule_id', - exception_lists: [ - { - id: '123', - list_id: 'my_list_id', - namespace_type: 'single', - type: ExceptionListTypeEnum.DETECTION, - }, - ], - }, - ], - }, - }, - jest.fn(), - ]); }); afterEach(() => { @@ -148,7 +80,7 @@ describe('When the add exception modal is opened', () => { }); describe('when the modal is loading', () => { - let wrapper: ReactWrapper; + let wrapper: ShallowWrapper; beforeEach(() => { // Mocks one of the hooks as loading mockFetchIndexPatterns.mockImplementation(() => ({ @@ -157,33 +89,79 @@ describe('When the add exception modal is opened', () => { getExtendedFields: () => Promise.resolve([]), })); - wrapper = mount( - <TestProviders> + wrapper = shallow( + <AddExceptionFlyout + rules={[{ ...getRulesSchemaMock() } as Rule]} + isBulkAction={false} + alertData={undefined} + isAlertDataLoading={undefined} + alertStatus={undefined} + isEndpointItem={false} + showAlertCloseOptions + onCancel={jest.fn()} + onConfirm={jest.fn()} + /> + ); + }); + + it('should show the loading spinner', () => { + expect(wrapper.find('EuiSkeletonText').exists()).toBeTruthy(); + }); + }); + + describe('exception list type of "endpoint"', () => { + describe('common functionality to test regardless of alert input', () => { + let wrapper: ShallowWrapper; + beforeEach(async () => { + wrapper = shallow( <AddExceptionFlyout - rules={[{ ...getRulesSchemaMock() } as Rule]} + rules={[ + { + ...getRulesSchemaMock(), + index: ['filebeat-*'], + exceptions_list: [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ], + } as Rule, + ]} isBulkAction={false} alertData={undefined} isAlertDataLoading={undefined} alertStatus={undefined} - isEndpointItem={false} + isEndpointItem showAlertCloseOptions onCancel={jest.fn()} onConfirm={jest.fn()} /> - </TestProviders> - ); - }); + ); + }); - it('should show the loading spinner', () => { - expect(wrapper.find('[data-test-subj="loadingAddExceptionFlyout"]').exists()).toBeTruthy(); - }); - }); + it('should render item name input', () => { + expect(wrapper.find('ExceptionsFlyoutMeta').exists()).toBeTruthy(); + }); - describe('exception list type of "endpoint"', () => { - describe('common functionality to test regardless of alert input', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - wrapper = mount( + it('should render the exception builder', () => { + expect(wrapper.find('ExceptionsConditions').exists()).toBeTruthy(); + }); + + it('does NOT render options to add exception to a rule or shared list', () => { + expect(wrapper.find('ExceptionsAddToRulesOrLists').exists()).toBeFalsy(); + }); + + it('should show a warning callout if wildcard is used', async () => { + mockUseFetchIndex.mockImplementation(() => [ + false, + { + indexPatterns: stubIndexPattern, + }, + ]); + + const mountWrapper = mount( <TestProviders> <AddExceptionFlyout rules={[ @@ -211,44 +189,6 @@ describe('When the add exception modal is opened', () => { /> </TestProviders> ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) - ); - }); - - it('displays proper flyout and button text', () => { - expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual( - i18n.ADD_ENDPOINT_EXCEPTION - ); - expect(wrapper.find('[data-test-subj="addExceptionConfirmButton"]').at(1).text()).toEqual( - i18n.ADD_ENDPOINT_EXCEPTION - ); - }); - - it('should render item name input', () => { - expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy(); - }); - - it('should render the exception builder', () => { - expect(wrapper.find('[data-test-subj="alertExceptionBuilder"]').exists()).toBeTruthy(); - }); - - it('does NOT render options to add exception to a rule or shared list', () => { - expect( - wrapper.find('[data-test-subj="exceptionItemAddToRuleOrListSection"]').exists() - ).toBeFalsy(); - }); - - it('should contain the endpoint specific documentation text', () => { - expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeTruthy(); - }); - - it('should NOT display the eql sequence callout', () => { - expect(wrapper.find('[data-test-subj="eqlSequenceCallout"]').exists()).not.toBeTruthy(); - }); - - it('should show a warning callout if wildcard is used', async () => { const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; await waitFor(() => callProps.onChange({ @@ -268,16 +208,23 @@ describe('When the add exception modal is opened', () => { }) ); - wrapper.update(); + mountWrapper.update(); expect( - wrapper.find('[data-test-subj="wildcardWithWrongOperatorCallout"]').exists() + mountWrapper.find('[data-test-subj="wildcardWithWrongOperatorCallout"]').exists() ).toBeTruthy(); }); }); describe('alert data is passed in', () => { let wrapper: ReactWrapper; - beforeEach(async () => { + beforeAll(async () => { + mockUseFetchIndex.mockImplementation(() => [ + false, + { + indexPatterns: stubIndexPattern, + }, + ]); + wrapper = mount( <TestProviders> <AddExceptionFlyout @@ -471,50 +418,38 @@ describe('When the add exception modal is opened', () => { }); describe('alert data NOT passed in', () => { - let wrapper: ReactWrapper; + let wrapper: ShallowWrapper; beforeEach(async () => { - wrapper = mount( - <TestProviders> - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - index: ['filebeat-*'], - exceptions_list: [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ], - } as Rule, - ]} - isBulkAction={false} - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} - isEndpointItem - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) + wrapper = shallow( + <AddExceptionFlyout + rules={[ + { + ...getRulesSchemaMock(), + index: ['filebeat-*'], + exceptions_list: [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ], + } as Rule, + ]} + isBulkAction={false} + alertData={undefined} + isAlertDataLoading={undefined} + alertStatus={undefined} + isEndpointItem + showAlertCloseOptions + onCancel={jest.fn()} + onConfirm={jest.fn()} + /> ); }); - it('should NOT render the close single alert checkbox', () => { - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeFalsy(); - }); - it('should render the os selection dropdown', () => { - expect(wrapper.find('[data-test-subj="osSelectionDropdown"]').exists()).toBeTruthy(); + expect(wrapper.find('ExceptionsConditions').prop('showOsTypeOptions')).toBeTruthy(); }); }); }); @@ -522,7 +457,7 @@ describe('When the add exception modal is opened', () => { describe('exception list type is NOT "endpoint" ("rule_default" or "detection")', () => { describe('common features to test regardless of alert input', () => { let wrapper: ReactWrapper; - beforeEach(async () => { + beforeAll(async () => { wrapper = mount( <TestProviders> <AddExceptionFlyout @@ -549,27 +484,10 @@ describe('When the add exception modal is opened', () => { ); }); - it('displays proper flyout and button text', () => { - expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual( - i18n.CREATE_RULE_EXCEPTION - ); - expect(wrapper.find('[data-test-subj="addExceptionConfirmButton"]').at(1).text()).toEqual( - i18n.CREATE_RULE_EXCEPTION - ); - }); - it('should NOT prepopulate items', () => { expect(defaultEndpointItems).not.toHaveBeenCalled(); }); - // button is disabled until there are exceptions, a name, and selection made on - // add to rule or lists section - it('has the add exception button disabled', () => { - expect( - wrapper.find('button[data-test-subj="addExceptionConfirmButton"]').getDOMNode() - ).toBeDisabled(); - }); - it('should render item name input', () => { expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy(); }); @@ -600,51 +518,6 @@ describe('When the add exception modal is opened', () => { }); }); - describe('alert data is passed in', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - wrapper = mount( - <TestProviders> - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - exceptions_list: [], - } as Rule, - ]} - isBulkAction={false} - alertData={alertDataMock} - isAlertDataLoading={false} - alertStatus="open" - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] }) - ); - }); - - it('should render the close single alert checkbox', () => { - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeTruthy(); - expect( - wrapper.find('input[data-test-subj="closeAlertOnAddExceptionCheckbox"]').getDOMNode() - ).not.toBeDisabled(); - }); - - it('should have the bulk close checkbox disabled', () => { - expect( - wrapper.find('input[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').getDOMNode() - ).toBeDisabled(); - }); - }); - describe('Auto populate rule exception', () => { beforeEach(() => { mockGetExceptionBuilderComponentLazy.mockImplementation((props) => { @@ -733,548 +606,123 @@ describe('When the add exception modal is opened', () => { expect(getAllByTestId('entryValue')[1]).toHaveTextContent('test/path'); }); }); - describe('bulk closeable alert data is passed in', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - mockUseFetchIndex.mockImplementation(() => [ - false, - { - indexPatterns: createStubIndexPattern({ - spec: { - id: '1234', - title: 'filebeat-*', - fields: { - 'event.code': { - name: 'event.code', - type: 'string', - aggregatable: true, - searchable: true, - }, - 'file.path.caseless': { - name: 'file.path.caseless', - type: 'string', - aggregatable: true, - searchable: true, - }, - subject_name: { - name: 'subject_name', - type: 'string', - aggregatable: true, - searchable: true, - }, - trusted: { - name: 'trusted', - type: 'string', - aggregatable: true, - searchable: true, - }, - 'file.hash.sha256': { - name: 'file.hash.sha256', - type: 'string', - aggregatable: true, - searchable: true, - }, - }, - }, - }), - }, - ]); - wrapper = mount( - <TestProviders> - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - index: ['filebeat-*'], - exceptions_list: [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ], - } as Rule, - ]} - isBulkAction={false} - alertData={alertDataMock} - isAlertDataLoading={false} - alertStatus="open" - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - </TestProviders> - ); - - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ - exceptionItems: [ - { - ...getExceptionListItemSchemaMock(), - entries: [ - { - field: 'file.hash.sha256', - operator: 'included', - type: 'match', - value: 'some value', - }, - ], - }, - ], - }) - ); - }); - - it('should render the close single alert checkbox', () => { - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeTruthy(); - expect( - wrapper.find('input[data-test-subj="closeAlertOnAddExceptionCheckbox"]').getDOMNode() - ).not.toBeDisabled(); - }); - - it('should have the bulk close checkbox enabled', () => { - expect( - wrapper.find('input[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').getDOMNode() - ).not.toBeDisabled(); - }); - - describe('when a "is in list" entry is added', () => { - it('should have the bulk close checkbox disabled', async () => { - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - - await waitFor(() => - callProps.onChange({ - exceptionItems: [ - ...callProps.exceptionListItems, - { - ...getExceptionListItemSchemaMock(), - entries: [ - { field: 'event.code', operator: 'included', type: 'list' }, - ] as EntriesArray, - }, - ], - }) - ); - - expect( - wrapper - .find('input[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]') - .getDOMNode() - ).toBeDisabled(); - }); - }); - }); - - describe('alert data NOT passed in', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - wrapper = mount( - <TestProviders> - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - index: ['filebeat-*'], - exceptions_list: [ - { - id: 'endpoint_list', - list_id: 'endpoint_list', - namespace_type: 'agnostic', - type: 'endpoint', - }, - ], - } as Rule, - ]} - isBulkAction={false} - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) - ); - }); - - it('should NOT render the close single alert checkbox', () => { - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeFalsy(); - }); - - it('should have the bulk close checkbox disabled', () => { - expect( - wrapper.find('input[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').getDOMNode() - ).toBeDisabled(); - }); - }); }); /* Say for example, from the lists management or lists details page */ describe('when no rules are passed in', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - wrapper = mount( - <TestProviders> - <AddExceptionFlyout - rules={null} - isBulkAction={false} - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] }) - ); - }); - - it('allows large value lists', () => { - expect(wrapper.find('ExceptionsConditions').prop('allowLargeValueLists')).toBeTruthy(); - }); - - it('defaults to selecting add to rule option, displaying rules selection table', () => { - expect(wrapper.find('[data-test-subj="addExceptionToRulesTable"]').exists()).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="selectRulesToAddToOptionRadio"] input').getDOMNode() - ).toHaveAttribute('checked'); - }); - }); - - /* Say for example, from the rule details page, exceptions tab, or from an alert */ - describe('when a single rule is passed in', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - wrapper = mount( - <TestProviders> - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - exceptions_list: [], - } as Rule, - ]} - isBulkAction={false} - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) - ); - }); - it('does not allow large value list selection for query rule', () => { - const shallowWrapper = shallow( - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - exceptions_list: [], - } as Rule, - ]} - isBulkAction={false} - alertData={alertDataMock} - isAlertDataLoading={false} - alertStatus="open" - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - ); - - expect(shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists')).toBeTruthy(); - }); - - it('does not allow large value list selection if EQL rule', () => { - const shallowWrapper = shallow( + let wrapper: ShallowWrapper; + beforeAll(async () => { + wrapper = shallow( <AddExceptionFlyout - rules={[ - { - ...getRulesEqlSchemaMock(), - exceptions_list: [], - } as Rule, - ]} + rules={null} isBulkAction={false} - alertData={alertDataMock} - isAlertDataLoading={false} - alertStatus="open" + alertData={undefined} + isAlertDataLoading={undefined} + alertStatus={undefined} isEndpointItem={false} showAlertCloseOptions onCancel={jest.fn()} onConfirm={jest.fn()} /> ); - - expect(shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists')).toBeFalsy(); }); - it('does not allow large value list selection if threshold rule', () => { - const shallowWrapper = shallow( - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - type: 'threshold', - } as Rule, - ]} - isBulkAction={false} - alertData={alertDataMock} - isAlertDataLoading={false} - alertStatus="open" - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - ); - - expect(shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists')).toBeFalsy(); + it('allows large value lists', () => { + expect(wrapper.find('ExceptionsConditions').prop('allowLargeValueLists')).toBeTruthy(); }); - it('does not allow large value list selection if new trems rule', () => { - const shallowWrapper = shallow( - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - type: 'new_terms', - } as Rule, - ]} - isBulkAction={false} - alertData={alertDataMock} - isAlertDataLoading={false} - alertStatus="open" - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> + it('defaults to selecting add to rule option, displaying rules selection table', () => { + expect(wrapper.find('ExceptionsAddToRulesOrLists').prop('selectedRadioOption')).toEqual( + 'select_rules_to_add_to' ); - - expect(shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists')).toBeFalsy(); - }); - - it('defaults to selecting add to rule radio option', () => { - expect( - wrapper.find('[data-test-subj="exceptionItemAddToRuleOrListSection"]').exists() - ).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="addToRuleOptionsRadio"] input').getDOMNode() - ).toBeChecked(); - }); - - it('disables add to shared lists option if rule has no shared exception lists attached already', () => { - expect( - wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() - ).toBeDisabled(); }); + }); - it('enables add to shared lists option if rule has shared list', () => { - wrapper = mount( - <TestProviders> + /* Say for example, from the rule details page, exceptions tab, or from an alert */ + describe('when a single rule is passed in', () => { + describe('large value list selection', () => { + it('does not allow large value list selection for query rule', () => { + const shallowWrapper = shallow( <AddExceptionFlyout rules={[ { ...getRulesSchemaMock(), - exceptions_list: [ - { - id: 'test', - list_id: 'test', - namespace_type: 'single', - type: 'detection', - }, - ], + exceptions_list: [], } as Rule, ]} isBulkAction={false} - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} + alertData={alertDataMock} + isAlertDataLoading={false} + alertStatus="open" isEndpointItem={false} showAlertCloseOptions onCancel={jest.fn()} onConfirm={jest.fn()} /> - </TestProviders> - ); + ); - expect( - wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() - ).toBeEnabled(); - }); - }); + expect( + shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists') + ).toBeTruthy(); + }); - /* Say for example, add exception item from rules bulk action */ - describe('when multiple rules are passed in - bulk action', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - wrapper = mount( - <TestProviders> + it('does not allow large value list selection if EQL rule', () => { + const shallowWrapper = shallow( <AddExceptionFlyout rules={[ { - ...getRulesSchemaMock(), + ...getRulesEqlSchemaMock(), exceptions_list: [], } as Rule, - { - ...getRulesSchemaMock(), - id: 'foo', - rule_id: 'foo', - exceptions_list: [ - { - id: 'bar', - list_id: 'bar', - namespace_type: 'single', - type: 'detection', - }, - ], - } as Rule, ]} - isBulkAction - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} + isBulkAction={false} + alertData={alertDataMock} + isAlertDataLoading={false} + alertStatus="open" isEndpointItem={false} showAlertCloseOptions onCancel={jest.fn()} onConfirm={jest.fn()} /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) - ); - }); - - it('allows large value lists', () => { - const shallowWrapper = shallow( - <AddExceptionFlyout - rules={[ - { - ...getRulesSchemaMock(), - exceptions_list: [], - } as Rule, - { - ...getRulesSchemaMock(), - id: 'foo', - rule_id: 'foo', - exceptions_list: [], - } as Rule, - ]} - isBulkAction - alertData={alertDataMock} - isAlertDataLoading={false} - alertStatus="open" - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - ); - - expect(shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists')).toBeTruthy(); - }); - - it('defaults to selecting add to rules radio option', () => { - expect( - wrapper.find('[data-test-subj="exceptionItemAddToRuleOrListSection"]').exists() - ).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="addToRulesOptionsRadio"] input').getDOMNode() - ).toBeChecked(); - }); + ); - it('disables add to shared lists option if rules have no shared lists in common', () => { - expect( - wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() - ).toBeDisabled(); - }); + expect( + shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists') + ).toBeFalsy(); + }); - it('enables add to shared lists option if rules have at least one shared list in common', () => { - wrapper = mount( - <TestProviders> + it('does not allow large value list selection if threshold rule', () => { + const shallowWrapper = shallow( <AddExceptionFlyout rules={[ { ...getRulesSchemaMock(), - exceptions_list: [ - { - id: 'bar', - list_id: 'bar', - namespace_type: 'single', - type: 'detection', - }, - ], - } as Rule, - { - ...getRulesSchemaMock(), - id: 'foo', - rule_id: 'foo', - exceptions_list: [ - { - id: 'bar', - list_id: 'bar', - namespace_type: 'single', - type: 'detection', - }, - ], + type: 'threshold', } as Rule, ]} - isBulkAction - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} + isBulkAction={false} + alertData={alertDataMock} + isAlertDataLoading={false} + alertStatus="open" isEndpointItem={false} showAlertCloseOptions onCancel={jest.fn()} onConfirm={jest.fn()} /> - </TestProviders> - ); + ); - expect( - wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() - ).toBeEnabled(); - }); - }); + expect( + shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists') + ).toBeFalsy(); + }); - describe('when there is an exception being created on a sequence eql rule type', () => { - let wrapper: ReactWrapper; - beforeEach(async () => { - wrapper = mount( - <TestProviders> + it('does not allow large value list selection if new trems rule', () => { + const shallowWrapper = shallow( <AddExceptionFlyout rules={[ { - ...getRulesEqlSchemaMock(), - query: - 'sequence [process where process.name = "test.exe"] [process where process.name = "explorer.exe"]', + ...getRulesSchemaMock(), + type: 'new_terms', } as Rule, ]} isBulkAction={false} @@ -1286,79 +734,113 @@ describe('When the add exception modal is opened', () => { onCancel={jest.fn()} onConfirm={jest.fn()} /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => - callProps.onChange({ exceptionItems: [getExceptionListItemSchemaMock()] }) - ); - }); + ); - it('should render the exception builder', () => { - expect(wrapper.find('[data-test-subj="alertExceptionBuilder"]').exists()).toBeTruthy(); + expect( + shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists') + ).toBeFalsy(); + }); }); - it('should not prepopulate endpoint items', () => { - expect(defaultEndpointItems).not.toHaveBeenCalled(); - }); + describe('add to rule/shared list selection', () => { + let wrapper: ReactWrapper; + beforeAll(async () => { + wrapper = mount( + <TestProviders> + <AddExceptionFlyout + rules={[ + { + ...getRulesSchemaMock(), + exceptions_list: [], + } as Rule, + ]} + isBulkAction={false} + alertData={undefined} + isAlertDataLoading={undefined} + alertStatus={undefined} + isEndpointItem={false} + showAlertCloseOptions + onCancel={jest.fn()} + onConfirm={jest.fn()} + /> + </TestProviders> + ); + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; + await waitFor(() => + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) + ); + }); - it('should render the close single alert checkbox', () => { - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeTruthy(); - }); + it('defaults to selecting add to rule radio option', () => { + expect( + wrapper.find('[data-test-subj="exceptionItemAddToRuleOrListSection"]').exists() + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="addToRuleOptionsRadio"] input').getDOMNode() + ).toBeChecked(); + }); - it('should have the bulk close checkbox disabled', () => { - expect( - wrapper.find('input[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').getDOMNode() - ).toBeDisabled(); - }); + it('disables add to shared lists option if rule has no shared exception lists attached already', () => { + expect( + wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() + ).toBeDisabled(); + }); - it('should display the eql sequence callout', () => { - expect(wrapper.find('[data-test-subj="eqlSequenceCallout"]').exists()).toBeTruthy(); + it('enables add to shared lists option if rule has shared list', () => { + wrapper = mount( + <TestProviders> + <AddExceptionFlyout + rules={[ + { + ...getRulesSchemaMock(), + exceptions_list: [ + { + id: 'test', + list_id: 'test', + namespace_type: 'single', + type: 'detection', + }, + ], + } as Rule, + ]} + isBulkAction={false} + alertData={undefined} + isAlertDataLoading={undefined} + alertStatus={undefined} + isEndpointItem={false} + showAlertCloseOptions + onCancel={jest.fn()} + onConfirm={jest.fn()} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() + ).toBeEnabled(); + }); }); }); - describe('error states', () => { - test('when there are exception builder errors submit button is disabled', async () => { - const wrapper = mount( - <TestProviders> + /* Say for example, add exception item from rules bulk action */ + describe('when multiple rules are passed in - bulk action', () => { + describe('large value lists selection', () => { + it('allows large value lists', () => { + const shallowWrapper = shallow( <AddExceptionFlyout rules={[ { ...getRulesSchemaMock(), + exceptions_list: [], } as Rule, - ]} - isBulkAction={false} - alertData={undefined} - isAlertDataLoading={undefined} - alertStatus={undefined} - isEndpointItem={false} - showAlertCloseOptions - onCancel={jest.fn()} - onConfirm={jest.fn()} - /> - </TestProviders> - ); - const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; - await waitFor(() => callProps.onChange({ exceptionItems: [], errorExists: true })); - expect( - wrapper.find('button[data-test-subj="addExceptionConfirmButton"]').getDOMNode() - ).toBeDisabled(); - }); - - test('when there is a comment error has submit button disabled', async () => { - const { getByLabelText, queryByText, getByTestId } = render( - <TestProviders> - <AddExceptionFlyout - rules={[ { - ...getRulesEqlSchemaMock(), - query: - 'sequence [process where process.name = "test.exe"] [process where process.name = "explorer.exe"]', + ...getRulesSchemaMock(), + id: 'foo', + rule_id: 'foo', + exceptions_list: [], } as Rule, ]} - isBulkAction={false} + isBulkAction alertData={alertDataMock} isAlertDataLoading={false} alertStatus="open" @@ -1367,25 +849,116 @@ describe('When the add exception modal is opened', () => { onCancel={jest.fn()} onConfirm={jest.fn()} /> - </TestProviders> - ); + ); + + expect( + shallowWrapper.find('ExceptionsConditions').prop('allowLargeValueLists') + ).toBeTruthy(); + }); + }); + + describe('add to rules/lists selection', () => { + let wrapper: ReactWrapper; + beforeAll(async () => { + wrapper = mount( + <TestProviders> + <AddExceptionFlyout + rules={[ + { + ...getRulesSchemaMock(), + exceptions_list: [], + } as Rule, + { + ...getRulesSchemaMock(), + id: 'foo', + rule_id: 'foo', + exceptions_list: [ + { + id: 'bar', + list_id: 'bar', + namespace_type: 'single', + type: 'detection', + }, + ], + } as Rule, + ]} + isBulkAction + alertData={undefined} + isAlertDataLoading={undefined} + alertStatus={undefined} + isEndpointItem={false} + showAlertCloseOptions + onCancel={jest.fn()} + onConfirm={jest.fn()} + /> + </TestProviders> + ); + const callProps = mockGetExceptionBuilderComponentLazy.mock.calls[0][0]; + await waitFor(() => + callProps.onChange({ exceptionItems: [...callProps.exceptionListItems] }) + ); + }); + it('defaults to selecting add to rules radio option', () => { + expect( + wrapper.find('[data-test-subj="exceptionItemAddToRuleOrListSection"]').exists() + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="addToRulesOptionsRadio"] input').getDOMNode() + ).toBeChecked(); + }); - const commentInput = getByLabelText('Comment Input'); + it('disables add to shared lists option if rules have no shared lists in common', () => { + expect( + wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() + ).toBeDisabled(); + }); - const commentErrorMessage = `The length of the comment is too long. The maximum length is ${MAX_COMMENT_LENGTH} characters.`; - expect(queryByText(commentErrorMessage)).toBeNull(); + it('enables add to shared lists option if rules have at least one shared list in common', () => { + wrapper = mount( + <TestProviders> + <AddExceptionFlyout + rules={[ + { + ...getRulesSchemaMock(), + exceptions_list: [ + { + id: 'bar', + list_id: 'bar', + namespace_type: 'single', + type: 'detection', + }, + ], + } as Rule, + { + ...getRulesSchemaMock(), + id: 'foo', + rule_id: 'foo', + exceptions_list: [ + { + id: 'bar', + list_id: 'bar', + namespace_type: 'single', + type: 'detection', + }, + ], + } as Rule, + ]} + isBulkAction + alertData={undefined} + isAlertDataLoading={undefined} + alertStatus={undefined} + isEndpointItem={false} + showAlertCloseOptions + onCancel={jest.fn()} + onConfirm={jest.fn()} + /> + </TestProviders> + ); - // Put comment with the length above maximum allowed - act(() => { - fireEvent.change(commentInput, { - target: { - value: [...new Array(MAX_COMMENT_LENGTH + 1).keys()].map((_) => 'a').join(''), - }, - }); - fireEvent.blur(commentInput); + expect( + wrapper.find('[data-test-subj="addToListsRadioOption"] input').getDOMNode() + ).toBeEnabled(); }); - expect(queryByText(commentErrorMessage)).not.toBeNull(); - expect(getByTestId('addExceptionConfirmButton')).toBeDisabled(); }); }); }); 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 a16fa6677b57b..85790713cfa3f 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 @@ -11,15 +11,11 @@ import { isEmpty } from 'lodash/fp'; import { EuiFlyout, - EuiFlyoutHeader, EuiTitle, - EuiFlyoutFooter, EuiFlyoutBody, EuiButton, - EuiButtonEmpty, EuiHorizontalRule, EuiSpacer, - EuiFlexGroup, EuiSkeletonText, EuiCallOut, EuiText, @@ -56,13 +52,15 @@ import type { Rule } from '../../../rule_management/logic/types'; import { ExceptionItemsFlyoutAlertsActions } from '../flyout_components/alerts_actions'; import { ExceptionsAddToRulesOrLists } from '../flyout_components/add_exception_to_rule_or_list'; import { useAddNewExceptionItems } from './use_add_new_exceptions'; -import { enrichNewExceptionItems } from '../flyout_components/utils'; import { useCloseAlertsFromExceptions } from '../../logic/use_close_alerts'; import { ruleTypesThatAllowLargeValueLists } from '../../utils/constants'; import { useInvalidateFetchRuleByIdQuery } from '../../../rule_management/api/hooks/use_fetch_rule_by_id_query'; import { ExceptionsExpireTime } from '../flyout_components/expire_time'; import { CONFIRM_WARNING_MODAL_LABELS } from '../../../../management/common/translations'; import { ArtifactConfirmModal } from '../../../../management/components/artifact_list_page/components/artifact_confirm_modal'; +import { ExceptionFlyoutFooter } from '../flyout_components/footer'; +import { ExceptionFlyoutHeader } from '../flyout_components/header'; +import { isSubmitDisabled, prepareNewItemsForSubmission, prepareToCloseAlerts } from './helpers'; const SectionHeader = styled(EuiTitle)` ${() => css` @@ -97,18 +95,6 @@ const FlyoutBodySection = styled(EuiFlyoutBody)` `} `; -const FlyoutHeader = styled(EuiFlyoutHeader)` - ${({ theme }) => css` - border-bottom: 1px solid ${theme.eui.euiColorLightShade}; - `} -`; - -const FlyoutFooterGroup = styled(EuiFlexGroup)` - ${({ theme }) => css` - padding: ${theme.eui.euiSizeS}; - `} -`; - export const AddExceptionFlyout = memo(function AddExceptionFlyout({ rules, isBulkAction, @@ -395,23 +381,16 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ if (submitNewExceptionItems == null) return; try { - const ruleDefaultOptions = ['add_to_rule', 'add_to_rules', 'select_rules_to_add_to']; - const addToRules = ruleDefaultOptions.includes(addExceptionToRadioSelection); - const addToSharedLists = - !!sharedListToAddTo?.length || - (addExceptionToRadioSelection === 'add_to_lists' && !isEmpty(exceptionListsToAddTo)); - const sharedLists = sharedListToAddTo?.length ? sharedListToAddTo : exceptionListsToAddTo; - - const items = enrichNewExceptionItems({ - itemName: exceptionItemName, - commentToAdd: newComment, - addToRules, - addToSharedLists, - sharedLists, + const { listsToAddTo, addToLists, addToRules, items } = prepareNewItemsForSubmission({ + sharedListToAddTo, + addExceptionToRadioSelection, + exceptionListsToAddTo, + exceptionItemName, + newComment, listType, - selectedOs: osTypesSelection, + osTypesSelection, expireTime, - items: exceptionItems, + exceptionItems, }); const addedItems = await submitNewExceptionItems({ @@ -419,16 +398,20 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ selectedRulesToAddTo, listType, addToRules: addToRules && !isEmpty(selectedRulesToAddTo), - addToSharedLists, - sharedLists, + addToSharedLists: addToLists, + sharedLists: listsToAddTo, }); - const alertIdToClose = closeSingleAlert && alertData ? alertData._id : undefined; - const ruleStaticIds = addToRules - ? selectedRulesToAddTo.map(({ rule_id: ruleId }) => ruleId) - : (rules ?? []).map(({ rule_id: ruleId }) => ruleId); + const { shouldCloseAlerts, alertIdToClose, ruleStaticIds } = prepareToCloseAlerts({ + alertData, + closeSingleAlert, + addToRules, + rules, + bulkCloseAlerts, + selectedRulesToAddTo, + }); - if (closeAlerts != null && !isEmpty(ruleStaticIds) && (bulkCloseAlerts || closeSingleAlert)) { + if (closeAlerts != null && shouldCloseAlerts) { await closeAlerts(ruleStaticIds, addedItems, alertIdToClose, bulkCloseIndex); } @@ -470,35 +453,20 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ } }, [wildcardWarningExists, submitException]); - const isSubmitButtonDisabled = useMemo( - (): boolean => - isSubmitting || - isClosingAlerts || - errorSubmitting != null || - exceptionItemName.trim() === '' || - exceptionItems.every((item) => item.entries.length === 0) || - itemConditionValidationErrorExists || - commentErrorExists || - expireErrorExists || - (addExceptionToRadioSelection === 'add_to_lists' && isEmpty(exceptionListsToAddTo)) || - (addExceptionToRadioSelection === 'select_rules_to_add_to' && - isEmpty(selectedRulesToAddTo) && - listType === ExceptionListTypeEnum.RULE_DEFAULT), - [ - isSubmitting, - isClosingAlerts, - errorSubmitting, - exceptionItemName, - exceptionItems, - itemConditionValidationErrorExists, - addExceptionToRadioSelection, - exceptionListsToAddTo, - expireErrorExists, - selectedRulesToAddTo, - listType, - commentErrorExists, - ] - ); + const isSubmitButtonDisabled = isSubmitDisabled({ + isSubmitting, + isClosingAlerts, + errorSubmitting, + exceptionItemName, + exceptionItems, + itemConditionValidationErrorExists, + commentErrorExists, + expireErrorExists, + addExceptionToRadioSelection, + selectedRulesToAddTo, + listType, + exceptionListsToAddTo, + }); const handleDismissError = useCallback((): void => { setErrorSubmitting(null); @@ -508,12 +476,6 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ onCancel(false); }, [onCancel]); - const addExceptionMessage = useMemo(() => { - return listType === ExceptionListTypeEnum.ENDPOINT - ? i18n.ADD_ENDPOINT_EXCEPTION - : i18n.CREATE_RULE_EXCEPTION; - }, [listType]); - const exceptionFlyoutTitleId = useGeneratedHtmlId({ prefix: 'exceptionFlyoutTitle', }); @@ -545,14 +507,11 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ // EUI TODO: This z-index override of EuiOverlayMask is a workaround, and ideally should be resolved with a cleaner UI/UX flow long-term maskProps={{ style: `z-index: ${(euiTheme.levels.flyout as number) + 3}` }} // we need this flyout to be above the timeline flyout (which has a z-index of 1002) > - <FlyoutHeader> - <EuiTitle> - <h2 id={exceptionFlyoutTitleId} data-test-subj="exceptionFlyoutTitle"> - {addExceptionMessage} - </h2> - </EuiTitle> - <EuiSpacer size="m" /> - </FlyoutHeader> + <ExceptionFlyoutHeader + listType={listType} + titleId={exceptionFlyoutTitleId} + dataTestSubjId={'exceptionFlyoutTitle'} + /> <FlyoutBodySection className="builder-section"> { // TODO: This is a quick fix to make sure that we do not lose conditions state on refetching index patterns via `useFetchIndexPatterns` @@ -660,22 +619,14 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ </> )} </FlyoutBodySection> - <EuiFlyoutFooter> - <FlyoutFooterGroup justifyContent="spaceBetween"> - <EuiButtonEmpty data-test-subj="cancelExceptionAddButton" onClick={handleCloseFlyout}> - {i18n.CANCEL} - </EuiButtonEmpty> - - <EuiButton - data-test-subj="addExceptionConfirmButton" - onClick={handleOnSubmit} - isDisabled={isSubmitButtonDisabled} - fill - > - {addExceptionMessage} - </EuiButton> - </FlyoutFooterGroup> - </EuiFlyoutFooter> + <ExceptionFlyoutFooter + listType={listType} + isSubmitButtonDisabled={isSubmitButtonDisabled} + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={handleOnSubmit} + handleCloseFlyout={handleCloseFlyout} + /> {showConfirmModal && confirmModal} </EuiFlyout> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.test.tsx index 5e64eca8df8a2..5c9f363bf1804 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/all_exception_items_table/utility_bar.test.tsx @@ -11,8 +11,21 @@ import { mount } from 'enzyme'; import { ExceptionsViewerUtility } from './utility_bar'; import { TestProviders } from '../../../../common/mock'; -// FLAKY: https://github.com/elastic/kibana/issues/185023 -describe.skip('ExceptionsViewerUtility', () => { +jest.mock('@kbn/i18n-react', () => { + const { i18n } = jest.requireActual('@kbn/i18n'); + i18n.init({ locale: 'en' }); + + const originalModule = jest.requireActual('@kbn/i18n-react'); + const FormattedRelative = jest.fn(); + FormattedRelative.mockImplementation(() => '20 hours ago'); + + return { + ...originalModule, + FormattedRelative, + }; +}); + +describe('ExceptionsViewerUtility', () => { it('it renders correct item counts', () => { const wrapper = mount( <TestProviders> @@ -55,7 +68,7 @@ describe.skip('ExceptionsViewerUtility', () => { ); expect(wrapper.find('[data-test-subj="exceptionsViewerLastUpdated"]').at(0).text()).toEqual( - 'Updated now' + 'Updated 20 hours ago' ); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx index 8fc6ddaa7d9b7..07723db415daf 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.test.tsx @@ -33,7 +33,6 @@ import { getExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/res import { useFetchIndexPatterns } from '../../logic/use_exception_flyout_data'; import { useCreateOrUpdateException } from '../../logic/use_create_update_exception'; import { useFindExceptionListReferences } from '../../logic/use_find_references'; -import * as i18n from './translations'; import { MAX_COMMENT_LENGTH } from '../../../../../common/constants'; const mockTheme = getMockTheme({ @@ -260,15 +259,6 @@ describe('When the edit exception modal is opened', () => { ); }); - it('displays proper flyout and button text', () => { - expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual( - i18n.EDIT_ENDPOINT_EXCEPTION_TITLE - ); - expect(wrapper.find('[data-test-subj="editExceptionConfirmButton"]').at(1).text()).toEqual( - i18n.EDIT_ENDPOINT_EXCEPTION_TITLE - ); - }); - it('should render item name input', () => { expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy(); }); @@ -466,15 +456,6 @@ describe('When the edit exception modal is opened', () => { ); }); - it('displays proper flyout and button text', () => { - expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual( - i18n.EDIT_EXCEPTION_TITLE - ); - expect(wrapper.find('[data-test-subj="editExceptionConfirmButton"]').at(1).text()).toEqual( - i18n.EDIT_EXCEPTION_TITLE - ); - }); - it('should render item name input', () => { expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy(); }); @@ -581,15 +562,6 @@ describe('When the edit exception modal is opened', () => { ); }); - it('displays proper flyout and button text', () => { - expect(wrapper.find('[data-test-subj="exceptionFlyoutTitle"]').at(1).text()).toEqual( - i18n.EDIT_EXCEPTION_TITLE - ); - expect(wrapper.find('[data-test-subj="editExceptionConfirmButton"]').at(1).text()).toEqual( - i18n.EDIT_EXCEPTION_TITLE - ); - }); - it('should render item name input', () => { expect(wrapper.find('[data-test-subj="exceptionFlyoutNameInput"]').exists()).toBeTruthy(); }); 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 b620814ee3fe6..b6336e3326e25 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 @@ -9,16 +9,10 @@ import { isEmpty } from 'lodash/fp'; import React, { useCallback, useEffect, useMemo, useReducer, useState } from 'react'; import styled, { css } from 'styled-components'; import { - EuiButton, - EuiButtonEmpty, EuiHorizontalRule, - EuiSpacer, - EuiFlyoutHeader, EuiFlyoutBody, - EuiFlexGroup, EuiTitle, EuiFlyout, - EuiFlyoutFooter, EuiSkeletonText, useGeneratedHtmlId, } from '@elastic/eui'; @@ -65,6 +59,8 @@ import { RULE_EXCEPTION, ENDPOINT_EXCEPTION } from '../../utils/translations'; import { ExceptionsExpireTime } from '../flyout_components/expire_time'; import { CONFIRM_WARNING_MODAL_LABELS } from '../../../../management/common/translations'; import { ArtifactConfirmModal } from '../../../../management/components/artifact_list_page/components/artifact_confirm_modal'; +import { ExceptionFlyoutFooter } from '../flyout_components/footer'; +import { ExceptionFlyoutHeader } from '../flyout_components/header'; interface EditExceptionFlyoutProps { list: ExceptionListSchema; @@ -76,12 +72,6 @@ interface EditExceptionFlyoutProps { onConfirm: (arg: boolean) => void; } -const FlyoutHeader = styled(EuiFlyoutHeader)` - ${({ theme }) => css` - border-bottom: 1px solid ${theme.eui.euiColorLightShade}; - `} -`; - const FlyoutBodySection = styled(EuiFlyoutBody)` ${() => css` &.builder-section { @@ -90,12 +80,6 @@ const FlyoutBodySection = styled(EuiFlyoutBody)` `} `; -const FlyoutFooterGroup = styled(EuiFlexGroup)` - ${({ theme }) => css` - padding: ${theme.eui.euiSizeS}; - `} -`; - const SectionHeader = styled(EuiTitle)` ${() => css` font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold}; @@ -357,14 +341,6 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({ } }, [wildcardWarningExists, handleSubmitException]); - const editExceptionMessage = useMemo( - () => - listType === ExceptionListTypeEnum.ENDPOINT - ? i18n.EDIT_ENDPOINT_EXCEPTION_TITLE - : i18n.EDIT_EXCEPTION_TITLE, - [listType] - ); - const isSubmitButtonDisabled = useMemo( () => isSubmitting || @@ -414,14 +390,11 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({ data-test-subj="editExceptionFlyout" aria-labelledby={exceptionFlyoutTitleId} > - <FlyoutHeader> - <EuiTitle> - <h2 id={exceptionFlyoutTitleId} data-test-subj="exceptionFlyoutTitle"> - {editExceptionMessage} - </h2> - </EuiTitle> - <EuiSpacer size="m" /> - </FlyoutHeader> + <ExceptionFlyoutHeader + listType={listType} + titleId={exceptionFlyoutTitleId} + dataTestSubjId={'exceptionFlyoutTitle'} + /> <FlyoutBodySection className="builder-section"> {isLoading && <EuiSkeletonText data-test-subj="loadingEditExceptionFlyout" lines={4} />} <ExceptionsFlyoutMeta @@ -499,22 +472,14 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({ </> )} </FlyoutBodySection> - <EuiFlyoutFooter> - <FlyoutFooterGroup justifyContent="spaceBetween"> - <EuiButtonEmpty data-test-subj="cancelExceptionEditButton" onClick={handleCloseFlyout}> - {i18n.CANCEL} - </EuiButtonEmpty> - - <EuiButton - data-test-subj="editExceptionConfirmButton" - onClick={handleOnSubmit} - isDisabled={isSubmitButtonDisabled} - fill - > - {editExceptionMessage} - </EuiButton> - </FlyoutFooterGroup> - </EuiFlyoutFooter> + <ExceptionFlyoutFooter + listType={listType} + isSubmitButtonDisabled={isSubmitButtonDisabled} + cancelButtonDataTestSubjId={'cancelExceptionEditButton'} + submitButtonDataTestSubjId={'editExceptionConfirmButton'} + handleOnSubmit={handleOnSubmit} + handleCloseFlyout={handleCloseFlyout} + /> {showConfirmModal && confirmModal} </EuiFlyout> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/translations.ts index 9839fa5ddc9de..6386cebf5dd48 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/translations.ts @@ -7,24 +7,6 @@ import { i18n } from '@kbn/i18n'; -export const CANCEL = i18n.translate('xpack.securitySolution.ruleExceptions.editException.cancel', { - defaultMessage: 'Cancel', -}); - -export const EDIT_EXCEPTION_TITLE = i18n.translate( - 'xpack.securitySolution.ruleExceptions.editException.editExceptionTitle', - { - defaultMessage: 'Edit rule exception', - } -); - -export const EDIT_ENDPOINT_EXCEPTION_TITLE = i18n.translate( - 'xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle', - { - defaultMessage: 'Edit endpoint exception', - } -); - export const EDIT_RULE_EXCEPTION_SUCCESS_TITLE = i18n.translate( 'xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle', { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.test.tsx index e7d56028a62a1..91a56fb1b61b4 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.test.tsx @@ -13,8 +13,13 @@ import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { ExceptionItemsFlyoutAlertsActions } from '.'; import { TestProviders } from '../../../../../common/mock'; import type { AlertData } from '../../../utils/types'; +import { useFetchIndex } from '../../../../../common/containers/source'; +import { useSignalIndex } from '../../../../../detections/containers/detection_engine/alerts/use_signal_index'; +import { stubIndexPattern } from '@kbn/data-plugin/common/stubs'; jest.mock('../../../../../common/lib/kibana'); +jest.mock('../../../../../common/containers/source'); +jest.mock('../../../../../detections/containers/detection_engine/alerts/use_signal_index'); const alertDataMock: AlertData = { '@timestamp': '1234567890', @@ -22,181 +27,364 @@ const alertDataMock: AlertData = { file: { path: 'test/path' }, }; +const mockUseSignalIndex = useSignalIndex as jest.Mock<Partial<ReturnType<typeof useSignalIndex>>>; +const mockUseFetchIndex = useFetchIndex as jest.Mock; + describe('ExceptionItemsFlyoutAlertsActions', () => { - it('it displays single alert close checkbox if alert status is not "closed" and "alertData" exists', () => { - const wrapper = mountWithIntl( - <TestProviders> - <ExceptionItemsFlyoutAlertsActions - exceptionListItems={[getExceptionListItemSchemaMock()]} - exceptionListType={ExceptionListTypeEnum.DETECTION} - shouldCloseSingleAlert={false} - shouldBulkCloseAlert={false} - disableBulkClose={false} - alertData={alertDataMock} - alertStatus="open" - onDisableBulkClose={jest.fn()} - onUpdateBulkCloseIndex={jest.fn()} - onBulkCloseCheckboxChange={jest.fn()} - onSingleAlertCloseCheckboxChange={jest.fn()} - isAlertDataLoading={false} - /> - </TestProviders> - ); - - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeTruthy(); - }); + beforeEach(() => { + mockUseSignalIndex.mockImplementation(() => ({ + loading: false, + signalIndexName: 'mock-siem-signals-index', + })); - it('it does not display single alert close checkbox if alert status is "closed"', () => { - const wrapper = mountWithIntl( - <TestProviders> - <ExceptionItemsFlyoutAlertsActions - exceptionListItems={[getExceptionListItemSchemaMock()]} - exceptionListType={ExceptionListTypeEnum.DETECTION} - shouldCloseSingleAlert={false} - shouldBulkCloseAlert={false} - disableBulkClose={false} - alertData={alertDataMock} - alertStatus="closed" - onDisableBulkClose={jest.fn()} - onUpdateBulkCloseIndex={jest.fn()} - onBulkCloseCheckboxChange={jest.fn()} - onSingleAlertCloseCheckboxChange={jest.fn()} - isAlertDataLoading={false} - /> - </TestProviders> - ); - - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeFalsy(); + mockUseFetchIndex.mockImplementation(() => [ + false, + { + indexPatterns: stubIndexPattern, + }, + ]); }); - it('it does not display single alert close checkbox if "alertData" does not exist', () => { - const wrapper = mountWithIntl( - <TestProviders> - <ExceptionItemsFlyoutAlertsActions - exceptionListItems={[getExceptionListItemSchemaMock()]} - exceptionListType={ExceptionListTypeEnum.DETECTION} - shouldCloseSingleAlert={false} - shouldBulkCloseAlert={false} - disableBulkClose={false} - alertData={undefined} - alertStatus="open" - onDisableBulkClose={jest.fn()} - onUpdateBulkCloseIndex={jest.fn()} - onBulkCloseCheckboxChange={jest.fn()} - onSingleAlertCloseCheckboxChange={jest.fn()} - isAlertDataLoading={false} - /> - </TestProviders> - ); - - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() - ).toBeFalsy(); + afterEach(() => { + jest.clearAllMocks(); }); - it('it displays bulk close checkbox', () => { - const wrapper = mountWithIntl( - <TestProviders> - <ExceptionItemsFlyoutAlertsActions - exceptionListItems={[getExceptionListItemSchemaMock()]} - exceptionListType={ExceptionListTypeEnum.DETECTION} - shouldCloseSingleAlert={false} - shouldBulkCloseAlert={false} - disableBulkClose={false} - alertData={alertDataMock} - alertStatus="open" - onDisableBulkClose={jest.fn()} - onUpdateBulkCloseIndex={jest.fn()} - onBulkCloseCheckboxChange={jest.fn()} - onSingleAlertCloseCheckboxChange={jest.fn()} - isAlertDataLoading={false} - /> - </TestProviders> - ); - - expect( - wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').exists() - ).toBeTruthy(); - }); + describe('Endpoint specific logic', () => { + it('it displays endpoint quarantine text if exception list type is "endpoint"', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.ENDPOINT} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); - it('it displays checkboxes disabled if "isAlertDataLoading" is "true"', () => { - const wrapper = mountWithIntl( - <TestProviders> - <ExceptionItemsFlyoutAlertsActions - exceptionListItems={[getExceptionListItemSchemaMock()]} - exceptionListType={ExceptionListTypeEnum.DETECTION} - shouldCloseSingleAlert={false} - shouldBulkCloseAlert={false} - disableBulkClose={false} - alertData={alertDataMock} - alertStatus="open" - onDisableBulkClose={jest.fn()} - onUpdateBulkCloseIndex={jest.fn()} - onBulkCloseCheckboxChange={jest.fn()} - onSingleAlertCloseCheckboxChange={jest.fn()} - isAlertDataLoading - /> - </TestProviders> - ); - - expect( - wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props().disabled - ).toBeTruthy(); - expect( - wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').at(0).props().disabled - ).toBeTruthy(); + expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeTruthy(); + }); }); - it('it displays bulk close checkbox disabled if "disableBulkCloseAlert" is "true"', () => { - const wrapper = mountWithIntl( - <TestProviders> - <ExceptionItemsFlyoutAlertsActions - exceptionListItems={[getExceptionListItemSchemaMock()]} - exceptionListType={ExceptionListTypeEnum.DETECTION} - shouldCloseSingleAlert={false} - shouldBulkCloseAlert={false} - disableBulkClose={true} - alertData={alertDataMock} - alertStatus="open" - onDisableBulkClose={jest.fn()} - onUpdateBulkCloseIndex={jest.fn()} - onBulkCloseCheckboxChange={jest.fn()} - onSingleAlertCloseCheckboxChange={jest.fn()} - isAlertDataLoading={false} - /> - </TestProviders> - ); - - expect( - wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props().disabled - ).toBeTruthy(); - expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeFalsy(); + describe('alert data exists', () => { + it('it displays single alert close checkbox if alert status is not "closed" and "alertData" exists', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled') + ).toBeFalsy(); + }); + + it('it displays single alert close checkbox disabled if "isAlertDataLoading" is true', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled') + ).toBeTruthy(); + }); + + it('it displays single alert close checkbox disabled if "isSignalIndexLoading" is true', () => { + mockUseSignalIndex.mockImplementation(() => ({ + loading: true, + signalIndexName: 'mock-siem-signals-index', + })); + + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled') + ).toBeTruthy(); + }); + + it('it displays single alert close checkbox disabled if "isSignalIndexPatternLoading" is true', () => { + mockUseFetchIndex.mockImplementation(() => [ + true, + { + indexPatterns: stubIndexPattern, + }, + ]); + + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"] input').prop('disabled') + ).toBeTruthy(); + }); + + it('it does not display single alert close checkbox if alert status is "closed"', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="closed" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() + ).toBeFalsy(); + }); }); - it('it displays endpoint quarantine text if exception list type is "endpoint"', () => { - const wrapper = mountWithIntl( - <TestProviders> - <ExceptionItemsFlyoutAlertsActions - exceptionListItems={[getExceptionListItemSchemaMock()]} - exceptionListType={ExceptionListTypeEnum.ENDPOINT} - shouldCloseSingleAlert={false} - shouldBulkCloseAlert={false} - disableBulkClose={false} - alertData={alertDataMock} - alertStatus="open" - onDisableBulkClose={jest.fn()} - onUpdateBulkCloseIndex={jest.fn()} - onBulkCloseCheckboxChange={jest.fn()} - onSingleAlertCloseCheckboxChange={jest.fn()} - isAlertDataLoading={false} - /> - </TestProviders> - ); - - expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeTruthy(); + describe('bulk close alert', () => { + it('it does not display single alert close checkbox if "alertData" does not exist', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={undefined} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').exists() + ).toBeFalsy(); + }); + + it('it displays bulk close checkbox', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').exists() + ).toBeTruthy(); + }); + + it('it displays checkboxes disabled if "isAlertDataLoading" is "true"', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props() + .disabled + ).toBeTruthy(); + expect( + wrapper.find('[data-test-subj="closeAlertOnAddExceptionCheckbox"]').at(0).props().disabled + ).toBeTruthy(); + }); + + it('it displays bulk close checkbox disabled if "disableBulkCloseAlert" is "true"', () => { + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={true} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props() + .disabled + ).toBeTruthy(); + expect(wrapper.find('[data-test-subj="addExceptionEndpointText"]').exists()).toBeFalsy(); + }); + + it('it displays bulk close checkbox disabled if "isSignalIndexLoading" is "true"', () => { + mockUseSignalIndex.mockImplementation(() => ({ + loading: true, + signalIndexName: 'mock-siem-signals-index', + })); + + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props() + .disabled + ).toBeTruthy(); + }); + + it('it displays bulk close checkbox disabled if "isSignalIndexPatternLoading" is "true"', () => { + mockUseFetchIndex.mockImplementation(() => [ + true, + { + indexPatterns: stubIndexPattern, + }, + ]); + + const wrapper = mountWithIntl( + <TestProviders> + <ExceptionItemsFlyoutAlertsActions + exceptionListItems={[getExceptionListItemSchemaMock()]} + exceptionListType={ExceptionListTypeEnum.DETECTION} + shouldCloseSingleAlert={false} + shouldBulkCloseAlert={false} + disableBulkClose={false} + alertData={alertDataMock} + alertStatus="open" + onDisableBulkClose={jest.fn()} + onUpdateBulkCloseIndex={jest.fn()} + onBulkCloseCheckboxChange={jest.fn()} + onSingleAlertCloseCheckboxChange={jest.fn()} + isAlertDataLoading={false} + /> + </TestProviders> + ); + + expect( + wrapper.find('[data-test-subj="bulkCloseAlertOnAddExceptionCheckbox"]').at(0).props() + .disabled + ).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.tsx index 5543a48a72858..0b9ebc9cd85d5 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/index.tsx @@ -15,7 +15,7 @@ import type { ExceptionsBuilderReturnExceptionItem } from '@kbn/securitysolution import { useSignalIndex } from '../../../../../detections/containers/detection_engine/alerts/use_signal_index'; import type { Status } from '../../../../../../common/api/detection_engine'; import { useFetchIndex } from '../../../../../common/containers/source'; -import { entryHasListType, entryHasNonEcsType } from './utils'; +import { shouldDisableBulkClose } from './utils'; import * as i18n from './translations'; import type { AlertData } from '../../../utils/types'; @@ -103,9 +103,7 @@ const ExceptionItemsFlyoutAlertsActionsComponent: React.FC< useEffect((): void => { if (isSignalIndexPatternLoading === false && isSignalIndexLoading === false) { onDisableBulkClose( - entryHasListType(exceptionListItems) || - entryHasNonEcsType(exceptionListItems, signalIndexPatterns) || - exceptionListItems.every((item) => item.entries.length === 0) + shouldDisableBulkClose({ items: exceptionListItems, signalIndexPatterns }) ); } }, [ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts index 777fec2fd135c..55171d830c4ae 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.test.ts @@ -8,9 +8,24 @@ import { getExceptionListItemSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_item_schema.mock'; import type { EntriesArray, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { entryHasNonEcsType, entryHasListType } from './utils'; +import { entryHasNonEcsType, entryHasListType, shouldDisableBulkClose } from './utils'; import type { DataViewBase } from '@kbn/es-query'; +const mockEcsIndexPattern = { + title: 'testIndex', + fields: [ + { + name: 'some.parentField', + }, + { + name: 'some.not.nested.field', + }, + { + name: 'nested.field', + }, + ], +} as DataViewBase; + describe('alerts_actions#utils', () => { describe('#entryHasListType', () => { test('it should return false with an empty array', () => { @@ -39,21 +54,6 @@ describe('alerts_actions#utils', () => { }); describe('#entryHasNonEcsType', () => { - const mockEcsIndexPattern = { - title: 'testIndex', - fields: [ - { - name: 'some.parentField', - }, - { - name: 'some.not.nested.field', - }, - { - name: 'nested.field', - }, - ], - } as DataViewBase; - test('it should return false with an empty array', () => { const payload: ExceptionListItemSchema[] = []; const result = entryHasNonEcsType(payload, mockEcsIndexPattern); @@ -78,4 +78,73 @@ describe('alerts_actions#utils', () => { expect(result).toEqual(true); }); }); + + describe('#shouldDisableBulkClose', () => { + it('returns true if items include large value lists', () => { + expect( + shouldDisableBulkClose({ + items: [ + { + ...getExceptionListItemSchemaMock(), + entries: [ + { + field: 'host.name', + list: { type: 'text', id: 'blob' }, + operator: 'included', + type: 'list', + }, + ], + }, + ], + signalIndexPatterns: mockEcsIndexPattern, + }) + ).toBeTruthy(); + }); + + it('returns true if items include non ECS types', () => { + expect( + shouldDisableBulkClose({ + items: [ + { + ...getExceptionListItemSchemaMock(), + entries: [{ field: 'some.nonEcsField' }] as EntriesArray, + }, + ], + signalIndexPatterns: mockEcsIndexPattern, + }) + ).toBeTruthy(); + }); + + it('returns true if all items have no entries', () => { + expect( + shouldDisableBulkClose({ + items: [ + { + ...getExceptionListItemSchemaMock(), + entries: [] as EntriesArray, + }, + ], + signalIndexPatterns: mockEcsIndexPattern, + }) + ).toBeTruthy(); + }); + + it('returns true if no items exist', () => { + expect( + shouldDisableBulkClose({ + items: [], + signalIndexPatterns: mockEcsIndexPattern, + }) + ).toBeTruthy(); + }); + + it('returns false if no large value list entries exist and all are ECS compliant', () => { + expect( + shouldDisableBulkClose({ + items: [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()], + signalIndexPatterns: mockEcsIndexPattern, + }) + ).toBeFalsy(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.ts index 03dc4e168cf0b..da393cae3bef9 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/alerts_actions/utils.ts @@ -58,3 +58,20 @@ export const entryHasNonEcsType = ( } return false; }; + +/** + * Determines whether the bulk close alerts option should be disabled. + */ +export const shouldDisableBulkClose = ({ + items, + signalIndexPatterns, +}: { + items: ExceptionsBuilderReturnExceptionItem[]; + signalIndexPatterns: DataViewBase; +}): boolean => { + return ( + entryHasListType(items) || + entryHasNonEcsType(items, signalIndexPatterns) || + items.every((item) => item.entries.length === 0) + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/index.test.tsx new file mode 100644 index 0000000000000..bb24ba61d1ab7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/index.test.tsx @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { TestProviders } from '../../../../../common/mock'; +import { ExceptionFlyoutFooter } from '.'; +import * as i18n from './translations'; + +describe('Exception flyout footer', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should render button disabled if "isSubmitButtonDisabled" is "true"', () => { + render( + <TestProviders> + <ExceptionFlyoutFooter + listType={ExceptionListTypeEnum.ENDPOINT} + isSubmitButtonDisabled + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={jest.fn()} + handleCloseFlyout={jest.fn()} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toBeDisabled(); + }); + + describe('"isEdit" is "false"', () => { + it('should render proper text when endpoint exception', () => { + render( + <TestProviders> + <ExceptionFlyoutFooter + listType={ExceptionListTypeEnum.ENDPOINT} + isSubmitButtonDisabled={false} + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={jest.fn()} + handleCloseFlyout={jest.fn()} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.ADD_ENDPOINT_EXCEPTION + ); + }); + + it('should render proper text when list type of "rule_default"', () => { + render( + <TestProviders> + <ExceptionFlyoutFooter + listType={ExceptionListTypeEnum.RULE_DEFAULT} + isSubmitButtonDisabled={false} + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={jest.fn()} + handleCloseFlyout={jest.fn()} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.CREATE_RULE_EXCEPTION + ); + }); + + it('should render proper text when list type of "detection"', () => { + render( + <TestProviders> + <ExceptionFlyoutFooter + listType={ExceptionListTypeEnum.DETECTION} + isSubmitButtonDisabled={false} + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={jest.fn()} + handleCloseFlyout={jest.fn()} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.CREATE_RULE_EXCEPTION + ); + }); + }); + + describe('"isEdit" is "true"', () => { + it('should render proper text when endpoint exception', () => { + render( + <TestProviders> + <ExceptionFlyoutFooter + isEdit + listType={ExceptionListTypeEnum.ENDPOINT} + isSubmitButtonDisabled={false} + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={jest.fn()} + handleCloseFlyout={jest.fn()} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.EDIT_ENDPOINT_EXCEPTION_TITLE + ); + }); + + it('should render proper text when list type of "rule_default"', () => { + render( + <TestProviders> + <ExceptionFlyoutFooter + isEdit + listType={ExceptionListTypeEnum.RULE_DEFAULT} + isSubmitButtonDisabled={false} + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={jest.fn()} + handleCloseFlyout={jest.fn()} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.EDIT_EXCEPTION_TITLE + ); + }); + + it('should render proper text when list type of "detection"', () => { + render( + <TestProviders> + <ExceptionFlyoutFooter + isEdit + listType={ExceptionListTypeEnum.DETECTION} + isSubmitButtonDisabled={false} + cancelButtonDataTestSubjId={'cancelExceptionAddButton'} + submitButtonDataTestSubjId={'addExceptionConfirmButton'} + handleOnSubmit={jest.fn()} + handleCloseFlyout={jest.fn()} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.EDIT_EXCEPTION_TITLE + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/index.tsx new file mode 100644 index 0000000000000..7e85f611d04e3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/index.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo } from 'react'; +import styled, { css } from 'styled-components'; + +import { EuiFlyoutFooter, EuiButton, EuiButtonEmpty, EuiFlexGroup } from '@elastic/eui'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import * as i18n from './translations'; + +const FlyoutFooterGroup = styled(EuiFlexGroup)` + ${({ theme }) => css` + padding: ${theme.eui.euiSizeS}; + `} +`; + +export interface ExceptionFlyoutFooterProps { + isEdit?: boolean; + listType: ExceptionListTypeEnum; + isSubmitButtonDisabled: boolean; + cancelButtonDataTestSubjId: string; + submitButtonDataTestSubjId: string; + handleOnSubmit: () => Promise<void> | undefined; + handleCloseFlyout: () => void; +} + +export const ExceptionFlyoutFooter = memo(function ExceptionFlyoutFooter({ + isEdit = false, + listType, + isSubmitButtonDisabled, + cancelButtonDataTestSubjId, + submitButtonDataTestSubjId, + handleOnSubmit, + handleCloseFlyout, +}: ExceptionFlyoutFooterProps) { + const addButtonMessage = useMemo(() => { + return listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.ADD_ENDPOINT_EXCEPTION + : i18n.CREATE_RULE_EXCEPTION; + }, [listType]); + + const editButtonMessage = useMemo(() => { + return listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.EDIT_ENDPOINT_EXCEPTION_TITLE + : i18n.EDIT_EXCEPTION_TITLE; + }, [listType]); + + const submitButtonMessage = isEdit ? editButtonMessage : addButtonMessage; + + return ( + <EuiFlyoutFooter> + <FlyoutFooterGroup justifyContent="spaceBetween"> + <EuiButtonEmpty data-test-subj={cancelButtonDataTestSubjId} onClick={handleCloseFlyout}> + {i18n.CANCEL} + </EuiButtonEmpty> + + <EuiButton + data-test-subj={submitButtonDataTestSubjId} + onClick={handleOnSubmit} + isDisabled={isSubmitButtonDisabled} + fill + > + {submitButtonMessage} + </EuiButton> + </FlyoutFooterGroup> + </EuiFlyoutFooter> + ); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/translations.ts new file mode 100644 index 0000000000000..7dbb3be7eb622 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/footer/translations.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CREATE_RULE_EXCEPTION = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addException', + { + defaultMessage: 'Add rule exception', + } +); + +export const ADD_ENDPOINT_EXCEPTION = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addEndpointException', + { + defaultMessage: 'Add Endpoint Exception', + } +); + +export const EDIT_ENDPOINT_EXCEPTION_TITLE = i18n.translate( + 'xpack.securitySolution.ruleExceptions..flyoutComponents.footer.editEndpointExceptionTitle', + { + defaultMessage: 'Edit endpoint exception', + } +); + +export const EDIT_EXCEPTION_TITLE = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.editExceptionTitle', + { + defaultMessage: 'Edit rule exception', + } +); + +export const CANCEL = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.cancel', + { + defaultMessage: 'Cancel', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/index.test.tsx new file mode 100644 index 0000000000000..f9f08231b2155 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/index.test.tsx @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; + +import { TestProviders } from '../../../../../common/mock'; +import { ExceptionFlyoutHeader } from '.'; +import * as i18n from './translations'; + +describe('Exception flyout header', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('"isEdit" is "false"', () => { + it('should render proper text when endpoint exception', () => { + render( + <TestProviders> + <ExceptionFlyoutHeader + listType={ExceptionListTypeEnum.ENDPOINT} + titleId={'someId'} + dataTestSubjId={'addExceptionConfirmButton'} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.ADD_ENDPOINT_EXCEPTION + ); + }); + + it('should render proper text when list type of "rule_default"', () => { + render( + <TestProviders> + <ExceptionFlyoutHeader + listType={ExceptionListTypeEnum.RULE_DEFAULT} + titleId={'someId'} + dataTestSubjId={'addExceptionConfirmButton'} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.CREATE_RULE_EXCEPTION + ); + }); + + it('should render proper text when list type of "detection"', () => { + render( + <TestProviders> + <ExceptionFlyoutHeader + listType={ExceptionListTypeEnum.DETECTION} + titleId={'someId'} + dataTestSubjId={'addExceptionConfirmButton'} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.CREATE_RULE_EXCEPTION + ); + }); + }); + + describe('"isEdit" is "true"', () => { + it('should render proper text when endpoint exception', () => { + render( + <TestProviders> + <ExceptionFlyoutHeader + isEdit + listType={ExceptionListTypeEnum.ENDPOINT} + titleId={'someId'} + dataTestSubjId={'addExceptionConfirmButton'} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.EDIT_ENDPOINT_EXCEPTION_TITLE + ); + }); + + it('should render proper text when list type of "rule_default"', () => { + render( + <TestProviders> + <ExceptionFlyoutHeader + isEdit + listType={ExceptionListTypeEnum.RULE_DEFAULT} + titleId={'someId'} + dataTestSubjId={'addExceptionConfirmButton'} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.EDIT_EXCEPTION_TITLE + ); + }); + + it('should render proper text when list type of "detection"', () => { + render( + <TestProviders> + <ExceptionFlyoutHeader + isEdit + listType={ExceptionListTypeEnum.DETECTION} + titleId={'someId'} + dataTestSubjId={'addExceptionConfirmButton'} + /> + </TestProviders> + ); + + expect(screen.getByTestId('addExceptionConfirmButton')).toHaveTextContent( + i18n.EDIT_EXCEPTION_TITLE + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/index.tsx new file mode 100644 index 0000000000000..185a4f4ec0148 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/index.tsx @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo } from 'react'; +import styled, { css } from 'styled-components'; + +import { EuiTitle, EuiSpacer, EuiFlyoutHeader } from '@elastic/eui'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import * as i18n from './translations'; + +const FlyoutHeader = styled(EuiFlyoutHeader)` + ${({ theme }) => css` + border-bottom: 1px solid ${theme.eui.euiColorLightShade}; + `} +`; + +export interface ExceptionFlyoutHeaderProps { + isEdit?: boolean; + listType: ExceptionListTypeEnum; + titleId: string; + dataTestSubjId: string; +} + +export const ExceptionFlyoutHeader = memo(function ExceptionFlyoutHeader({ + isEdit = false, + listType, + titleId, + dataTestSubjId, +}: ExceptionFlyoutHeaderProps) { + const addTitle = useMemo(() => { + return listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.ADD_ENDPOINT_EXCEPTION + : i18n.CREATE_RULE_EXCEPTION; + }, [listType]); + + const editTitle = useMemo(() => { + return listType === ExceptionListTypeEnum.ENDPOINT + ? i18n.EDIT_ENDPOINT_EXCEPTION_TITLE + : i18n.EDIT_EXCEPTION_TITLE; + }, [listType]); + + const title = isEdit ? editTitle : addTitle; + + return ( + <FlyoutHeader> + <EuiTitle> + <h2 id={titleId} data-test-subj={dataTestSubjId}> + {title} + </h2> + </EuiTitle> + <EuiSpacer size="m" /> + </FlyoutHeader> + ); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/translations.ts new file mode 100644 index 0000000000000..7dbb3be7eb622 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/flyout_components/header/translations.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CREATE_RULE_EXCEPTION = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addException', + { + defaultMessage: 'Add rule exception', + } +); + +export const ADD_ENDPOINT_EXCEPTION = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.addEndpointException', + { + defaultMessage: 'Add Endpoint Exception', + } +); + +export const EDIT_ENDPOINT_EXCEPTION_TITLE = i18n.translate( + 'xpack.securitySolution.ruleExceptions..flyoutComponents.footer.editEndpointExceptionTitle', + { + defaultMessage: 'Edit endpoint exception', + } +); + +export const EDIT_EXCEPTION_TITLE = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.editExceptionTitle', + { + defaultMessage: 'Edit rule exception', + } +); + +export const CANCEL = i18n.translate( + 'xpack.securitySolution.ruleExceptions.flyoutComponents.footer.cancel', + { + defaultMessage: 'Cancel', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts index 57f6c5f4e8305..3513c9a21aa7b 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/api.ts @@ -13,9 +13,6 @@ import { INTERNAL_ALERTING_API_FIND_RULES_PATH } from '@kbn/alerting-plugin/comm import { BASE_ACTION_API_PATH } from '@kbn/actions-plugin/common'; import type { ActionType, AsApiContract } from '@kbn/actions-plugin/common'; import type { ActionResult } from '@kbn/actions-plugin/server'; -import type { BulkInstallPackagesResponse } from '@kbn/fleet-plugin/common'; -import { epmRouteService } from '@kbn/fleet-plugin/common'; -import type { InstallPackageResponse } from '@kbn/fleet-plugin/common/types'; import { convertRulesFilterToKQL } from '../../../../common/detection_engine/rule_management/rule_filtering'; import type { UpgradeSpecificRulesRequest, @@ -48,6 +45,7 @@ import { } from '../../../../common/constants'; import { + BOOTSTRAP_PREBUILT_RULES_URL, GET_PREBUILT_RULES_STATUS_URL, PERFORM_RULE_INSTALLATION_URL, PERFORM_RULE_UPGRADE_URL, @@ -81,6 +79,7 @@ import type { RulesSnoozeSettingsMap, UpdateRulesProps, } from '../logic/types'; +import type { BootstrapPrebuiltRulesResponse } from '../../../../common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.gen'; /** * Create provided Rule @@ -594,66 +593,6 @@ export const addRuleExceptions = async ({ } ); -export interface InstallFleetPackageProps { - packageName: string; - packageVersion: string; - prerelease?: boolean; - force?: boolean; -} - -/** - * Install a Fleet package from the registry - * - * @param packageName Name of the package to install - * @param packageVersion Version of the package to install - * @param prerelease Whether to install a prerelease version of the package - * @param force Whether to force install the package. If false, the package will only be installed if it is not already installed - * - * @returns The response from the Fleet API - */ -export const installFleetPackage = ({ - packageName, - packageVersion, - prerelease = false, - force = true, -}: InstallFleetPackageProps): Promise<InstallPackageResponse> => { - return KibanaServices.get().http.post<InstallPackageResponse>( - epmRouteService.getInstallPath(packageName, packageVersion), - { - query: { prerelease }, - version: '2023-10-31', - body: JSON.stringify({ force }), - } - ); -}; - -export interface BulkInstallFleetPackagesProps { - packages: string[]; - prerelease?: boolean; -} - -/** - * Install multiple Fleet packages from the registry - * - * @param packages Array of package names to install - * @param prerelease Whether to install prerelease versions of the packages - * - * @returns The response from the Fleet API - */ -export const bulkInstallFleetPackages = ({ - packages, - prerelease = false, -}: BulkInstallFleetPackagesProps): Promise<BulkInstallPackagesResponse> => { - return KibanaServices.get().http.post<BulkInstallPackagesResponse>( - epmRouteService.getBulkInstallPath(), - { - query: { prerelease }, - version: '2023-10-31', - body: JSON.stringify({ packages }), - } - ); -}; - /** * NEW PREBUILT RULES ROUTES START HERE! 👋 * USE THESE ONES! THEY'RE THE NICE ONES, PROMISE! @@ -759,3 +698,9 @@ export const performUpgradeSpecificRules = async ( pick_version: 'TARGET', // Setting fixed 'TARGET' temporarily for Milestone 2 }), }); + +export const bootstrapPrebuiltRules = async (): Promise<BootstrapPrebuiltRulesResponse> => + KibanaServices.get().http.fetch(BOOTSTRAP_PREBUILT_RULES_URL, { + method: 'POST', + version: '1', + }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_bulk_install_fleet_packages_mutation.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_bootstrap_prebuilt_rules.ts similarity index 59% rename from x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_bulk_install_fleet_packages_mutation.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_bootstrap_prebuilt_rules.ts index d47f4799fcc30..9f2b810138a6c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_bulk_install_fleet_packages_mutation.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_bootstrap_prebuilt_rules.ts @@ -4,39 +4,37 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EPM_API_ROUTES } from '@kbn/fleet-plugin/common'; -import type { BulkInstallPackagesResponse } from '@kbn/fleet-plugin/common/types'; import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; +import { BOOTSTRAP_PREBUILT_RULES_URL } from '../../../../../common/api/detection_engine'; +import type { BootstrapPrebuiltRulesResponse } from '../../../../../common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.gen'; import { PREBUILT_RULES_PACKAGE_NAME } from '../../../../../common/detection_engine/constants'; -import type { BulkInstallFleetPackagesProps } from '../api'; -import { bulkInstallFleetPackages } from '../api'; +import { bootstrapPrebuiltRules } from '../api'; import { useInvalidateFetchPrebuiltRulesInstallReviewQuery } from './prebuilt_rules/use_fetch_prebuilt_rules_install_review_query'; import { useInvalidateFetchPrebuiltRulesStatusQuery } from './prebuilt_rules/use_fetch_prebuilt_rules_status_query'; import { useInvalidateFetchPrebuiltRulesUpgradeReviewQuery } from './prebuilt_rules/use_fetch_prebuilt_rules_upgrade_review_query'; -export const BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY = [ - 'POST', - EPM_API_ROUTES.BULK_INSTALL_PATTERN, -]; +export const BOOTSTRAP_PREBUILT_RULES_KEY = ['POST', BOOTSTRAP_PREBUILT_RULES_URL]; -export const useBulkInstallFleetPackagesMutation = ( - options?: UseMutationOptions<BulkInstallPackagesResponse, Error, BulkInstallFleetPackagesProps> +export const useBootstrapPrebuiltRulesMutation = ( + options?: UseMutationOptions<BootstrapPrebuiltRulesResponse, Error> ) => { const invalidatePrePackagedRulesStatus = useInvalidateFetchPrebuiltRulesStatusQuery(); const invalidatePrebuiltRulesInstallReview = useInvalidateFetchPrebuiltRulesInstallReviewQuery(); const invalidatePrebuiltRulesUpdateReview = useInvalidateFetchPrebuiltRulesUpgradeReviewQuery(); - return useMutation((props: BulkInstallFleetPackagesProps) => bulkInstallFleetPackages(props), { + return useMutation(() => bootstrapPrebuiltRules(), { ...options, - mutationKey: BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY, + mutationKey: BOOTSTRAP_PREBUILT_RULES_KEY, onSettled: (...args) => { const response = args[0]; - const rulesPackage = response?.items.find( - (item) => item.name === PREBUILT_RULES_PACKAGE_NAME - ); - if (rulesPackage && 'result' in rulesPackage && rulesPackage.result.status === 'installed') { - // The rules package was installed/updated, so invalidate the pre-packaged rules status query + if ( + response?.packages.find((pkg) => pkg.name === PREBUILT_RULES_PACKAGE_NAME)?.status === + 'installed' + ) { + // Invalidate other pre-packaged rules related queries. We need to do + // that only in case the prebuilt rules package was installed indicating + // that there might be new rules to install. invalidatePrePackagedRulesStatus(); invalidatePrebuiltRulesInstallReview(); invalidatePrebuiltRulesUpdateReview(); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_install_fleet_package_mutation.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_install_fleet_package_mutation.ts deleted file mode 100644 index e4f88d8b9c3ef..0000000000000 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/use_install_fleet_package_mutation.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 { EPM_API_ROUTES } from '@kbn/fleet-plugin/common'; -import type { InstallPackageResponse } from '@kbn/fleet-plugin/common/types'; -import type { UseMutationOptions } from '@tanstack/react-query'; -import { useMutation } from '@tanstack/react-query'; -import { PREBUILT_RULES_PACKAGE_NAME } from '../../../../../common/detection_engine/constants'; -import type { InstallFleetPackageProps } from '../api'; -import { installFleetPackage } from '../api'; -import { useInvalidateFetchPrebuiltRulesInstallReviewQuery } from './prebuilt_rules/use_fetch_prebuilt_rules_install_review_query'; -import { useInvalidateFetchPrebuiltRulesStatusQuery } from './prebuilt_rules/use_fetch_prebuilt_rules_status_query'; -import { useInvalidateFetchPrebuiltRulesUpgradeReviewQuery } from './prebuilt_rules/use_fetch_prebuilt_rules_upgrade_review_query'; - -export const INSTALL_FLEET_PACKAGE_MUTATION_KEY = [ - 'POST', - EPM_API_ROUTES.INSTALL_FROM_REGISTRY_PATTERN, -]; - -export const useInstallFleetPackageMutation = ( - options?: UseMutationOptions<InstallPackageResponse, Error, InstallFleetPackageProps> -) => { - const invalidatePrePackagedRulesStatus = useInvalidateFetchPrebuiltRulesStatusQuery(); - const invalidatePrebuiltRulesInstallReview = useInvalidateFetchPrebuiltRulesInstallReviewQuery(); - const invalidatePrebuiltRulesUpdateReview = useInvalidateFetchPrebuiltRulesUpgradeReviewQuery(); - - return useMutation((props: InstallFleetPackageProps) => installFleetPackage(props), { - ...options, - mutationKey: INSTALL_FLEET_PACKAGE_MUTATION_KEY, - onSettled: (...args) => { - const { packageName } = args[2]; - if (packageName === PREBUILT_RULES_PACKAGE_NAME) { - // Invalidate the pre-packaged rules status query as there might be new rules to install - invalidatePrePackagedRulesStatus(); - invalidatePrebuiltRulesInstallReview(); - invalidatePrebuiltRulesUpdateReview(); - } - - if (options?.onSettled) { - options.onSettled(...args); - } - }, - }); -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/diff_view.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/diff_view.tsx index 69503c7062372..ba3195edab73a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/diff_view.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/diff_view.tsx @@ -25,6 +25,7 @@ import type { HunkTokens, } from 'react-diff-view'; import unidiff from 'unidiff'; +import type { Change } from 'diff'; import { useEuiTheme, COLOR_MODES_STANDARD } from '@elastic/eui'; import { Hunks } from './hunks'; import { markEdits, DiffMethod } from './mark_edits'; @@ -111,15 +112,32 @@ const renderGutter: RenderGutter = ({ change }) => { return null; }; +/** + * Converts an array of Change objects into a "unified diff" string. + * + * Takes an array of changes (as provided by the jsdiff library) and converts it into a "unified diff" string. + * + * @param {Change[]} changes - An array of changes between two strings. + * @returns {string} A unified diff string representing the changes. + */ +const convertChangesToUnifiedDiffString = (changes: Change[]): string => { + const unifiedDiff: string = unidiff.formatLines(changes, { + context: 3, + }); + + return unifiedDiff; +}; + 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); + const changes: Change[] = 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 + "convertChangesToUnifiedDiffString" converts an array of Change objects into a single "unified diff" string. + More info about the "unified diff" format: https://en.wikipedia.org/wiki/Diff#Unified_format + Unified diff is a string with change markers added. Looks something like: ` @@ -3,16 +3,15 @@ @@ -129,9 +147,7 @@ const convertToDiffFile = (oldSource: string, newSource: string) => { "history_window_start": "now-14d", ` */ - const unifiedDiff: string = unidiff.formatLines(changes, { - context: 3, - }); + const unifiedDiff: string = convertChangesToUnifiedDiffString(changes); /* "parseDiff" converts a unified diff string into a gitdiff-parser File object. @@ -154,18 +170,34 @@ const CustomStyles: FC<PropsWithChildren<unknown>> = ({ children }) => { padding: 0 ${euiTheme.size.l} 0 ${euiTheme.size.m}; } + /* Gutter - a narrow column on the left-hand side that displays either a "+" or a "-" */ .${TABLE_CLASS_NAME} .diff-gutter-col { width: ${euiTheme.size.xl}; } + /* + Hide the redundant second gutter column in "unified" view. + Hiding it with "display: none" would break the layout, so we set its width to 0 and make its content invisible. + */ + .${TABLE_CLASS_NAME}.diff-unified .diff-gutter-col + .diff-gutter-col { + width: 0; + } + .${TABLE_CLASS_NAME}.diff-unified .diff-gutter + .diff-gutter { + visibility: hidden; + } + /* Vertical line separating two sides of the diff view */ .${GUTTER_CLASS_NAME}:nth-child(3) { border-left: 1px solid ${euiTheme.colors.mediumShade}; } + .${GUTTER_CLASS_NAME}.diff-gutter-delete, .${GUTTER_CLASS_NAME}.diff-gutter-insert { + font-weight: bold; + text-align: center; + } + /* 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 { @@ -174,7 +206,6 @@ const CustomStyles: FC<PropsWithChildren<unknown>> = ({ children }) => { /* 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 { @@ -228,12 +259,14 @@ interface DiffViewProps extends Partial<DiffProps> { oldSource: string; newSource: string; diffMethod?: DiffMethod; + viewType?: 'split' | 'unified'; } export const DiffView = ({ oldSource, newSource, diffMethod = DiffMethod.WORDS, + viewType = 'split', }: DiffViewProps) => { /* "react-diff-view" components consume diffs not as a strings, but as something they call "hunks". @@ -272,6 +305,7 @@ export const DiffView = ({ Passing 'add' or 'delete' would skip rendering one of the sides in split view. */ diffType={diffFile.type} + viewType={viewType} hunks={hunks} renderGutter={renderGutter} tokens={tokens} 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 index d7fa438700e97..0f57677af362c 100644 --- 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 @@ -5,14 +5,20 @@ * 2.0. */ -declare module 'unidiff' { - interface Change { - count?: number | undefined; - value: string; - added?: boolean | undefined; - removed?: boolean | undefined; - } +interface Change { + count?: number | undefined; + value: string; + added?: boolean | undefined; + removed?: boolean | undefined; +} + +type ADDED = 'ADDED'; +type REMOVED = 'REMOVED'; +type UNMODIFIED = 'UNMODIFIED'; +type LineChangeType = ADDED | REMOVED | UNMODIFIED; + +declare module 'unidiff' { export interface FormatOptions { context?: number; } @@ -21,3 +27,28 @@ declare module 'unidiff' { export function formatLines(line: Change[], options?: FormatOptions): string; } + +declare module 'unidiff/hunk' { + export const ADDED: ADDED; + export const REMOVED: REMOVED; + export const UNMODIFIED: UNMODIFIED; + + export type ChangeWithType = Change & { type: LineChangeType }; + + export interface LineChange { + type: LineChangeType; + text: string; + unified(): string; + } + + export interface UniDiffHunk { + aoff: number; + boff: number; + changes: LineChange[]; + unified(): string; + } + + export function lineChanges(change: ChangeWithType): LineChange[]; + + export function hunk(aOffset: number, bOffset: number, lchanges: LineChange[]): UniDiffHunk; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/comparison_side.stories.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/comparison_side.stories.tsx new file mode 100644 index 0000000000000..37ddb71dce9f7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/comparison_side.stories.tsx @@ -0,0 +1,315 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { Story } from '@storybook/react'; +import { ComparisonSide } from './comparison_side'; +import type { + ThreeWayDiff, + DiffableAllFields, + RuleKqlQuery, +} from '../../../../../../../common/api/detection_engine'; +import { + ThreeWayDiffOutcome, + ThreeWayMergeOutcome, + ThreeWayDiffConflict, + KqlQueryType, +} from '../../../../../../../common/api/detection_engine'; +export default { + component: ComparisonSide, + title: 'Rule Management/Prebuilt Rules/Upgrade Flyout/ThreeWayDiff/ComparisonSide', + argTypes: { + fieldName: { + control: 'text', + description: 'Field name to compare', + }, + fieldThreeWayDiff: { + control: 'object', + description: 'Field ThreeWayDiff', + }, + resolvedValue: { + control: 'text', + description: 'Resolved value', + }, + }, +}; + +interface TemplateProps<FieldName extends keyof DiffableAllFields> { + fieldName: FieldName; + fieldThreeWayDiff: ThreeWayDiff<DiffableAllFields[FieldName]>; + resolvedValue?: DiffableAllFields[FieldName]; +} + +const Template: Story<TemplateProps<keyof DiffableAllFields>> = (args) => { + return ( + <ComparisonSide + fieldName={args.fieldName} + fieldThreeWayDiff={args.fieldThreeWayDiff} + resolvedValue={args.resolvedValue} + /> + ); +}; + +export const NoBaseVersion = Template.bind({}); +NoBaseVersion.args = { + fieldName: 'rule_name_override', + fieldThreeWayDiff: { + has_base_version: false, + base_version: undefined, + current_version: { + field_name: 'rule.name', + }, + target_version: undefined, + merged_version: undefined, + diff_outcome: ThreeWayDiffOutcome.MissingBaseCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + has_update: true, + conflict: ThreeWayDiffConflict.SOLVABLE, + }, +}; + +export const WithResolvedValue = Template.bind({}); +WithResolvedValue.args = { + fieldName: 'risk_score', + fieldThreeWayDiff: { + has_base_version: true, + base_version: 10, + current_version: 40, + target_version: 20, + merged_version: 40, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_update: true, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }, + resolvedValue: 35, +}; +WithResolvedValue.argTypes = { + resolvedValue: { + control: 'number', + }, +}; + +/* Optional field becomes defined - was undefined in base version, but was then defined by user in current version */ +export const OptionalFieldBecomesDefined = Template.bind({}); +OptionalFieldBecomesDefined.args = { + fieldName: 'timestamp_override', + fieldThreeWayDiff: { + has_base_version: true, + base_version: undefined, + current_version: { + field_name: 'event.ingested', + fallback_disabled: true, + }, + target_version: undefined, + merged_version: { + field_name: 'event.ingested', + fallback_disabled: true, + }, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_update: false, + conflict: ThreeWayDiffConflict.NONE, + }, +}; + +export const SubfieldsWithLabels = Template.bind({}); + +const subfieldsWithLabelsThreeWayDiff: ThreeWayDiff<RuleKqlQuery> = { + has_base_version: true, + base_version: { + type: KqlQueryType.inline_query, + query: 'event.agent_id_status: *', + language: 'kuery', + filters: [], + }, + current_version: { + type: KqlQueryType.inline_query, + query: 'event.agent_id_status: *', + language: 'kuery', + filters: [], + }, + target_version: { + type: KqlQueryType.saved_query, + saved_query_id: 'e355ef26-45f5-40f1-bbb7-5176ecf07d5c', + }, + merged_version: { + type: KqlQueryType.saved_query, + saved_query_id: 'e355ef26-45f5-40f1-bbb7-5176ecf07d5c', + }, + diff_outcome: ThreeWayDiffOutcome.StockValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Target, + has_update: true, + conflict: ThreeWayDiffConflict.NONE, +}; + +SubfieldsWithLabels.args = { + fieldName: 'kql_query', + fieldThreeWayDiff: subfieldsWithLabelsThreeWayDiff, +}; + +/* Field type changes - in this example "kql_query" field was "inline" in base version, but became "saved" in the current version */ +export const FieldTypeChanges = Template.bind({}); + +const fieldTypeChangesThreeWayDiff: ThreeWayDiff<RuleKqlQuery> = { + has_base_version: true, + base_version: { + type: KqlQueryType.inline_query, + query: 'event.agent_id_status: *', + language: 'kuery', + filters: [], + }, + current_version: { + type: KqlQueryType.saved_query, + saved_query_id: 'e355ef26-45f5-40f1-bbb7-5176ecf07d5c', + }, + target_version: { + type: KqlQueryType.inline_query, + query: 'event.agent_id_status: *', + language: 'kuery', + filters: [], + }, + merged_version: { + type: KqlQueryType.saved_query, + saved_query_id: 'e355ef26-45f5-40f1-bbb7-5176ecf07d5c', + }, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueNoUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_update: false, + conflict: ThreeWayDiffConflict.NONE, +}; + +FieldTypeChanges.args = { + fieldName: 'kql_query', + fieldThreeWayDiff: fieldTypeChangesThreeWayDiff, +}; + +export const SingleLineStringSubfieldChanges = Template.bind({}); +SingleLineStringSubfieldChanges.args = { + fieldName: 'name', + fieldThreeWayDiff: { + has_base_version: true, + base_version: 'Prebuilt rule', + current_version: 'Customized prebuilt rule', + target_version: 'Prebuilt rule with new changes', + merged_version: 'Customized prebuilt rule', + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_update: true, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }, +}; + +export const MultiLineStringSubfieldChanges = Template.bind({}); +MultiLineStringSubfieldChanges.args = { + fieldName: 'note', + fieldThreeWayDiff: { + has_base_version: true, + base_version: 'My description.\f\nThis is a second\u2001 line.\f\nThis is a third line.', + current_version: + 'My GREAT description.\f\nThis is a second\u2001 line.\f\nThis is a third line.', + target_version: 'My description.\f\nThis is a second\u2001 line.\f\nThis is a GREAT line.', + merged_version: + 'My GREAT description.\f\nThis is a second\u2001 line.\f\nThis is a GREAT line.', + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, + has_update: true, + conflict: ThreeWayDiffConflict.SOLVABLE, + }, +}; + +export const NumberSubfieldChanges = Template.bind({}); +NumberSubfieldChanges.args = { + fieldName: 'risk_score', + fieldThreeWayDiff: { + has_base_version: true, + base_version: 33, + current_version: 43, + target_version: 53, + merged_version: 43, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_update: true, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }, +}; + +export const ArraySubfieldChanges = Template.bind({}); +ArraySubfieldChanges.args = { + fieldName: 'tags', + fieldThreeWayDiff: { + has_base_version: true, + base_version: ['one', 'two', 'three'], + current_version: ['two', 'three', 'four', 'five'], + target_version: ['one', 'three', 'four', 'six'], + merged_version: ['three', 'four', 'five', 'six'], + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_update: true, + conflict: ThreeWayDiffConflict.SOLVABLE, + }, +}; + +export const QuerySubfieldChanges = Template.bind({}); +QuerySubfieldChanges.args = { + fieldName: 'kql_query', + fieldThreeWayDiff: { + has_base_version: true, + base_version: { + type: KqlQueryType.inline_query, + query: + 'event.action:("Directory Service Changes" or "directory-service-object-modified") and event.code:5136 and\n winlog.event_data.OperationType:"%%14674" and\n winlog.event_data.ObjectClass:"user" and\n winlog.event_data.AttributeLDAPDisplayName:"servicePrincipalName"\n', + language: 'kuery', + filters: [], + }, + current_version: { + type: KqlQueryType.inline_query, + query: + 'event.action:("Directory Service Changes" or "directory-service-object-modified") and event.code:5136 and\n winlog.event_data.OperationType:"%%14674" or winlog.event_data.OperationType:"%%14675" and\n winlog.event_data.ObjectClass:"user" and\n winlog.event_data.AttributeLDAPDisplayName:"serviceSecondaryName"\n', + language: 'kuery', + filters: [], + }, + target_version: { + type: KqlQueryType.inline_query, + query: + 'event.action:("Directory Service Changes" or "Directory Service Modifications" or "directory-service-object-modified") and event.code:5136 and\n winlog.event_data.OperationType:"%%14674" and\n winlog.event_data.ObjectClass:"user" and\n winlog.event_data.AttributeLDAPDisplayName:"servicePrincipalName"\n', + language: 'kuery', + filters: [], + }, + merged_version: { + type: KqlQueryType.inline_query, + query: + 'event.action:("Directory Service Changes" or "directory-service-object-modified") and event.code:5136 and\n winlog.event_data.OperationType:"%%14674" or winlog.event_data.OperationType:"%%14675" and\n winlog.event_data.ObjectClass:"user" and\n winlog.event_data.AttributeLDAPDisplayName:"serviceSecondaryName"\n', + language: 'kuery', + filters: [], + }, + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Current, + has_update: true, + conflict: ThreeWayDiffConflict.NON_SOLVABLE, + }, +}; + +export const SetupGuideSubfieldChanges = Template.bind({}); +SetupGuideSubfieldChanges.args = { + fieldName: 'setup', + fieldThreeWayDiff: { + has_base_version: true, + base_version: + '## Setup\n\nThis rule requires data coming in from one of the following integrations:\n- Elastic Defend\n- Auditbeat\n\n### Elastic Defend Integration Setup\nElastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.\n\n#### Prerequisite Requirements:\n- Fleet is required for Elastic Defend.\n- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).\n\n#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:\n- Go to the Kibana home page and click "Add integrations".\n- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.\n- Click "Add Elastic Defend".\n- Configure the integration name and optionally add a description.\n- Select the type of environment you want to protect, either "Traditional Endpoints" or "Cloud Workloads".\n- Select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).\n- We suggest selecting "Complete EDR (Endpoint Detection and Response)" as a configuration setting, that provides "All events; all preventions"\n- Enter a name for the agent policy in "New agent policy name". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.\nFor more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).\n- Click "Save and Continue".\n- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.\nFor more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).\n\n### Auditbeat Setup\nAuditbeat is a lightweight shipper that you can install on your servers to audit the activities of users and processes on your systems. For example, you can use Auditbeat to collect and centralize audit events from the Linux Audit Framework. You can also use Auditbeat to detect changes to critical files, like binaries and configuration files, and identify potential security policy violations.\n\n#### The following steps should be executed in order to add the Auditbeat on a Linux System:\n- Elastic provides repositories available for APT and YUM-based distributions. Note that we provide binary packages, but no source packages.\n- To install the APT and YUM repositories follow the setup instructions in this [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setup-repositories.html).\n- To run Auditbeat on Docker follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-docker.html).\n- To run Auditbeat on Kubernetes follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-kubernetes.html).\n- For complete “Setup and Run Auditbeat” information refer to the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setting-up-and-running.html).\n', + current_version: + '## Setup\n\nThis rule requires data coming in from one of the following integrations:\n- Elastic Defend\n- Auditbeat\n- Custom Windows Event Logs\n\n### Elastic Defend Integration Setup\nElastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.\n\n#### Prerequisite Requirements:\n- Fleet is required for Elastic Defend.\n- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).\n\n#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:\n- Go to the Kibana home page and click "Add integrations".\n- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.\n- Click "Add Elastic Defend".\n- Configure the integration name and optionally add a description.\n- Select the type of environment you want to protect.\n- Select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).\n- Enter a name for the agent policy in "New agent policy name". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.\nFor more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).\n- Click "Save and Continue".\n- The rule is now ready to run.\n- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.\nFor more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).\n\n### Auditbeat Setup\nAuditbeat is a lightweight shipper that you can install on your servers to audit the activities of users and processes on your systems. For example, you can use Auditbeat to collect and centralize audit events from the Linux Audit Framework. You can also use Auditbeat to detect changes to critical files, like binaries and configuration files, and identify potential security policy violations.\n\n#### The following steps should be executed in order to add the Auditbeat on a Linux System:\n- Elastic provides repositories available for AXT and YUM-based distributions. Note that we provide binary packages, but no source packages.\n- To install the AXT and YUM repositories follow the setup instructions in this [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setup-repositories.html).\n- To run Auditbeat on Docker follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-docker.html).\n- To run Auditbeat on Kubernetes follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-kubernetes.html).\n- For complete “Setup and Run Auditbeat” information refer to the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setting-up-and-running.html).\n', + target_version: + '## Setup\n\nThis rule requires data coming in from one of the following integrations:\n- Elastic Defend\n- Auditbeat\n\n### Elastic Defend Integration Setup\nElastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.\n\n#### Prerequisite Requirements:\n- Fleet is required for Elastic Defend.\n- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).\n\n#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:\n- Go to the Kibana home page and click "Add integrations".\n- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.\n- Click "Add Elastic Defend".\n- Configure the integration name and optionally add a description.\n- Select the type of environment you want to protect, either "Traditional Endpoints" or "Cloud Workloads".\n- Carefully select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).\n- We suggest selecting "Complete EDR (Endpoint Detection and Response)" as a configuration setting, that provides "All events; all preventions"\n- Enter a title for the agent policy in "New agent policy title". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.\nFor more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).\n- Click "Save and Continue".\n- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.\nFor more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).\n\n### Auditbeat Setup\nAuditbeat is a lightweight shipper that you can install on your servers to audit the activities of users and processes on your systems. You can use Auditbeat to detect changes to critical files, like binaries and configuration files, and identify potential security policy violations.\n\n#### The following steps should be executed in order to add the Auditbeat on a Linux System:\n- Elastic provides repositories available for APT and YUM-based distributions. Note that we provide binary packages, but no source packages.\n- To install the APT and YUM repositories follow the setup instructions in this [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setup-repositories.html).\n- To run Auditbeat on Docker follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-docker.html).\n- To run Auditbeat on Kubernetes follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-kubernetes.html).\n- For complete “Setup and Run Auditbeat” information refer to the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setting-up-and-running.html).\n- Good luck!\n', + merged_version: + '## Setup\n\nThis rule requires data coming in from one of the following integrations:\n- Elastic Defend\n- Auditbeat\n- Custom Windows Event Logs\n\n### Elastic Defend Integration Setup\nElastic Defend is integrated into the Elastic Agent using Fleet. Upon configuration, the integration allows the Elastic Agent to monitor events on your host and send data to the Elastic Security app.\n\n#### Prerequisite Requirements:\n- Fleet is required for Elastic Defend.\n- To configure Fleet Server refer to the [documentation](https://www.elastic.co/guide/en/fleet/current/fleet-server.html).\n\n#### The following steps should be executed in order to add the Elastic Defend integration on a Linux System:\n- Go to the Kibana home page and click "Add integrations".\n- In the query bar, search for "Elastic Defend" and select the integration to see more details about it.\n- Click "Add Elastic Defend".\n- Configure the integration name and optionally add a description.\n- Select the type of environment you want to protect.\n- Carefully select a configuration preset. Each preset comes with different default settings for Elastic Agent, you can further customize these later by configuring the Elastic Defend integration policy. [Helper guide](https://www.elastic.co/guide/en/security/current/configure-endpoint-integration-policy.html).\n- Enter a title for the agent policy in "New agent policy title". If other agent policies already exist, you can click the "Existing hosts" tab and select an existing policy instead.\nFor more details on Elastic Agent configuration settings, refer to the [helper guide](https://www.elastic.co/guide/en/fleet/8.10/agent-policy.html).\n- Click "Save and Continue".\n- The rule is now ready to run.\n- To complete the integration, select "Add Elastic Agent to your hosts" and continue to the next section to install the Elastic Agent on your hosts.\nFor more details on Elastic Defend refer to the [helper guide](https://www.elastic.co/guide/en/security/current/install-endpoint.html).\n\n### Auditbeat Setup\nAuditbeat is a lightweight shipper that you can install on your servers to audit the activities of users and processes on your systems. You can use Auditbeat to detect changes to critical files, like binaries and configuration files, and identify potential security policy violations.\n\n#### The following steps should be executed in order to add the Auditbeat on a Linux System:\n- Elastic provides repositories available for AXT and YUM-based distributions. Note that we provide binary packages, but no source packages.\n- To install the AXT and YUM repositories follow the setup instructions in this [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setup-repositories.html).\n- To run Auditbeat on Docker follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-docker.html).\n- To run Auditbeat on Kubernetes follow the setup instructions in the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/running-on-kubernetes.html).\n- For complete “Setup and Run Auditbeat” information refer to the [helper guide](https://www.elastic.co/guide/en/beats/auditbeat/current/setting-up-and-running.html).\n- Good luck!\n', + diff_outcome: ThreeWayDiffOutcome.CustomizedValueCanUpdate, + merge_outcome: ThreeWayMergeOutcome.Merged, + has_update: true, + conflict: ThreeWayDiffConflict.SOLVABLE, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/comparison_side.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/comparison_side.tsx new file mode 100644 index 0000000000000..3a6f3e366d848 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/comparison_side.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { EuiSpacer } from '@elastic/eui'; +import { VersionsPicker } from '../versions_picker/versions_picker'; +import type { Version } from '../versions_picker/constants'; +import { SelectedVersions } from '../versions_picker/constants'; +import { pickFieldValueForVersion } from './utils'; +import type { + DiffableAllFields, + ThreeWayDiff, +} from '../../../../../../../common/api/detection_engine'; +import { getSubfieldChanges } from './get_subfield_changes'; +import { SubfieldChanges } from './subfield_changes'; + +interface ComparisonSideProps<FieldName extends keyof DiffableAllFields> { + fieldName: FieldName; + fieldThreeWayDiff: ThreeWayDiff<DiffableAllFields[FieldName]>; + resolvedValue?: DiffableAllFields[FieldName]; +} + +export function ComparisonSide<FieldName extends keyof DiffableAllFields>({ + fieldName, + fieldThreeWayDiff, + resolvedValue, +}: ComparisonSideProps<FieldName>) { + const [selectedVersions, setSelectedVersions] = useState<SelectedVersions>( + SelectedVersions.CurrentFinal + ); + + const [oldVersionType, newVersionType] = selectedVersions.split('_') as [Version, Version]; + + const oldFieldValue = pickFieldValueForVersion(oldVersionType, fieldThreeWayDiff, resolvedValue); + const newFieldValue = pickFieldValueForVersion(newVersionType, fieldThreeWayDiff, resolvedValue); + + const subfieldChanges = getSubfieldChanges(fieldName, oldFieldValue, newFieldValue); + + return ( + <> + <VersionsPicker + hasBaseVersion={fieldThreeWayDiff.has_base_version} + selectedVersions={selectedVersions} + onChange={setSelectedVersions} + /> + <EuiSpacer size="m" /> + <SubfieldChanges fieldName={fieldName} subfieldChanges={subfieldChanges} /> + </> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/constants.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/constants.ts new file mode 100644 index 0000000000000..d848fc7bb1dfa --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/constants.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DiffableAllFields } from '../../../../../../../common/api/detection_engine'; + +export const FIELDS_WITH_SUBFIELDS: Array<keyof DiffableAllFields> = [ + 'data_source', + 'kql_query', + 'eql_query', + 'esql_query', + 'threat_query', + 'rule_schedule', + 'rule_name_override', + 'timestamp_override', + 'timeline_template', + 'building_block', + 'threshold', +]; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/building_block.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/building_block.ts new file mode 100644 index 0000000000000..4cb1ad66f8a90 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/building_block.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForBuildingBlock = ( + oldFieldValue?: DiffableAllFields['building_block'], + newFieldValue?: DiffableAllFields['building_block'] +): SubfieldChange[] => { + const oldType = stringifyToSortedJson(oldFieldValue?.type); + const newType = stringifyToSortedJson(newFieldValue?.type); + + if (oldType !== newType) { + return [ + { + subfieldName: 'type', + oldSubfieldValue: oldType, + newSubfieldValue: newType, + }, + ]; + } + + return []; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/data_source.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/data_source.ts new file mode 100644 index 0000000000000..f887f623362a4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/data_source.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForDataSource = ( + oldFieldValue?: DiffableAllFields['data_source'], + newFieldValue?: DiffableAllFields['data_source'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldType = stringifyToSortedJson(oldFieldValue?.type); + const newType = stringifyToSortedJson(newFieldValue?.type); + + if (oldType !== newType) { + changes.push({ + subfieldName: 'type', + oldSubfieldValue: oldType, + newSubfieldValue: newType, + }); + } + + const oldIndexPatterns = stringifyToSortedJson( + oldFieldValue?.type === 'index_patterns' ? oldFieldValue?.index_patterns : '' + ); + const newIndexPatterns = stringifyToSortedJson( + newFieldValue?.type === 'index_patterns' ? newFieldValue?.index_patterns : '' + ); + + if (oldIndexPatterns !== newIndexPatterns) { + changes.push({ + subfieldName: 'index_patterns', + oldSubfieldValue: oldIndexPatterns, + newSubfieldValue: newIndexPatterns, + }); + } + + const oldDataViewId = stringifyToSortedJson( + oldFieldValue?.type === 'data_view' ? oldFieldValue?.data_view_id : '' + ); + const newDataViewId = stringifyToSortedJson( + newFieldValue?.type === 'data_view' ? newFieldValue?.data_view_id : '' + ); + + if (oldDataViewId !== newDataViewId) { + changes.push({ + subfieldName: 'data_view_id', + oldSubfieldValue: oldDataViewId, + newSubfieldValue: newDataViewId, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/eql_query.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/eql_query.ts new file mode 100644 index 0000000000000..25a4dff97dd21 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/eql_query.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForEqlQuery = ( + oldFieldValue?: DiffableAllFields['eql_query'], + newFieldValue?: DiffableAllFields['eql_query'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldQuery = stringifyToSortedJson(oldFieldValue?.query); + const newQuery = stringifyToSortedJson(newFieldValue?.query); + + if (oldQuery !== newQuery) { + changes.push({ + subfieldName: 'query', + oldSubfieldValue: oldQuery, + newSubfieldValue: newQuery, + }); + } + + const oldFilters = stringifyToSortedJson(oldFieldValue?.filters); + const newFilters = stringifyToSortedJson(newFieldValue?.filters); + + if (oldFilters !== newFilters) { + changes.push({ + subfieldName: 'filters', + oldSubfieldValue: oldFilters, + newSubfieldValue: newFilters, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/esql_query.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/esql_query.ts new file mode 100644 index 0000000000000..dd99f8de8579e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/esql_query.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForEsqlQuery = ( + oldFieldValue?: DiffableAllFields['esql_query'], + newFieldValue?: DiffableAllFields['esql_query'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldQuery = stringifyToSortedJson(oldFieldValue?.query); + const newQuery = stringifyToSortedJson(newFieldValue?.query); + + if (oldQuery !== newQuery) { + changes.push({ + subfieldName: 'query', + oldSubfieldValue: oldQuery, + newSubfieldValue: newQuery, + }); + } + + const oldLanguage = stringifyToSortedJson(oldFieldValue?.language); + const newLanguage = stringifyToSortedJson(oldFieldValue?.language); + + if (oldLanguage !== newLanguage) { + changes.push({ + subfieldName: 'language', + oldSubfieldValue: oldLanguage, + newSubfieldValue: newLanguage, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/index.ts new file mode 100644 index 0000000000000..c3628d74176c3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import { stringifyToSortedJson } from '../utils'; +import { getSubfieldChangesForDataSource } from './data_source'; +import { getSubfieldChangesForKqlQuery } from './kql_query'; +import { getSubfieldChangesForEqlQuery } from './eql_query'; +import { getSubfieldChangesForEsqlQuery } from './esql_query'; +import { getSubfieldChangesForThreatQuery } from './threat_query'; +import { getSubfieldChangesForRuleSchedule } from './rule_schedule'; +import { getSubfieldChangesForRuleNameOverride } from './rule_name_override'; +import { getSubfieldChangesForTimestampOverride } from './timestamp_override'; +import { getSubfieldChangesForTimelineTemplate } from './timeline_template'; +import { getSubfieldChangesForBuildingBlock } from './building_block'; +import { getSubfieldChangesForThreshold } from './threshold'; +import type { SubfieldChanges } from '../types'; + +/** + * Splits a field into subfields and returns the changes between the old and new subfield values. + * + * @param fieldName - The name of the field for which subfield changes are to be computed. + * @param oldFieldValue - The old value of the field. + * @param newFieldValue - The new value of the field. + * @returns - An array of subfield changes. + */ +export const getSubfieldChanges = <FieldName extends keyof DiffableAllFields>( + fieldName: FieldName, + oldFieldValue?: DiffableAllFields[FieldName], + newFieldValue?: DiffableAllFields[FieldName] +): SubfieldChanges => { + switch (fieldName) { + /* + Typecasting `oldFieldValue` and `newFieldValue` to corresponding field + type `DiffableAllFields[*]` is required here since `oldFieldValue` and + `newFieldValue` concrete types depend on `fieldName` but TS doesn't track that. + */ + case 'data_source': + return getSubfieldChangesForDataSource( + oldFieldValue as DiffableAllFields['data_source'], + newFieldValue as DiffableAllFields['data_source'] + ); + case 'kql_query': + return getSubfieldChangesForKqlQuery( + oldFieldValue as DiffableAllFields['kql_query'], + newFieldValue as DiffableAllFields['kql_query'] + ); + case 'eql_query': + return getSubfieldChangesForEqlQuery( + oldFieldValue as DiffableAllFields['eql_query'], + newFieldValue as DiffableAllFields['eql_query'] + ); + case 'esql_query': + return getSubfieldChangesForEsqlQuery( + oldFieldValue as DiffableAllFields['esql_query'], + newFieldValue as DiffableAllFields['esql_query'] + ); + case 'threat_query': + return getSubfieldChangesForThreatQuery( + oldFieldValue as DiffableAllFields['threat_query'], + newFieldValue as DiffableAllFields['threat_query'] + ); + case 'rule_schedule': + return getSubfieldChangesForRuleSchedule( + oldFieldValue as DiffableAllFields['rule_schedule'], + newFieldValue as DiffableAllFields['rule_schedule'] + ); + case 'rule_name_override': + return getSubfieldChangesForRuleNameOverride( + oldFieldValue as DiffableAllFields['rule_name_override'], + newFieldValue as DiffableAllFields['rule_name_override'] + ); + case 'timestamp_override': + return getSubfieldChangesForTimestampOverride( + oldFieldValue as DiffableAllFields['timestamp_override'], + newFieldValue as DiffableAllFields['timestamp_override'] + ); + case 'timeline_template': + return getSubfieldChangesForTimelineTemplate( + oldFieldValue as DiffableAllFields['timeline_template'], + newFieldValue as DiffableAllFields['timeline_template'] + ); + case 'building_block': + return getSubfieldChangesForBuildingBlock( + oldFieldValue as DiffableAllFields['building_block'], + newFieldValue as DiffableAllFields['building_block'] + ); + case 'threshold': + return getSubfieldChangesForThreshold( + oldFieldValue as DiffableAllFields['threshold'], + newFieldValue as DiffableAllFields['threshold'] + ); + default: + const oldFieldValueStringified = stringifyToSortedJson(oldFieldValue); + const newFieldValueStringified = stringifyToSortedJson(newFieldValue); + + if (oldFieldValueStringified === newFieldValueStringified) { + return []; + } + + return [ + { + subfieldName: fieldName, + oldSubfieldValue: oldFieldValueStringified, + newSubfieldValue: newFieldValueStringified, + }, + ]; + } +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/kql_query.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/kql_query.ts new file mode 100644 index 0000000000000..4bd96d212ba0f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/kql_query.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { stringifyToSortedJson } from '../utils'; +import type { RuleKqlQuery } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForKqlQuery = ( + oldFieldValue?: RuleKqlQuery, + newFieldValue?: RuleKqlQuery +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldType = stringifyToSortedJson(oldFieldValue?.type); + const newType = stringifyToSortedJson(newFieldValue?.type); + + if (oldType !== newType) { + changes.push({ + subfieldName: 'type', + oldSubfieldValue: oldType, + newSubfieldValue: newType, + }); + } + + const oldQuery = stringifyToSortedJson( + oldFieldValue?.type === 'inline_query' ? oldFieldValue?.query : '' + ); + const newQuery = stringifyToSortedJson( + newFieldValue?.type === 'inline_query' ? newFieldValue?.query : '' + ); + + if (oldQuery !== newQuery) { + changes.push({ + subfieldName: 'query', + oldSubfieldValue: oldQuery, + newSubfieldValue: newQuery, + }); + } + + const oldLanguage = stringifyToSortedJson( + oldFieldValue?.type === 'inline_query' ? oldFieldValue?.language : '' + ); + const newLanguage = stringifyToSortedJson( + newFieldValue?.type === 'inline_query' ? newFieldValue?.language : '' + ); + + if (oldLanguage !== newLanguage) { + changes.push({ + subfieldName: 'language', + oldSubfieldValue: oldLanguage, + newSubfieldValue: newLanguage, + }); + } + + const oldFilters = stringifyToSortedJson( + oldFieldValue?.type === 'inline_query' ? oldFieldValue?.filters : '' + ); + const newFilters = stringifyToSortedJson( + newFieldValue?.type === 'inline_query' ? newFieldValue?.filters : '' + ); + + if (oldFilters !== newFilters) { + changes.push({ + subfieldName: 'filters', + oldSubfieldValue: oldFilters, + newSubfieldValue: newFilters, + }); + } + + const oldSavedQueryId = stringifyToSortedJson( + oldFieldValue?.type === 'saved_query' ? oldFieldValue?.saved_query_id : '' + ); + const newSavedQueryId = stringifyToSortedJson( + newFieldValue?.type === 'saved_query' ? newFieldValue?.saved_query_id : '' + ); + + if (oldSavedQueryId !== newSavedQueryId) { + changes.push({ + subfieldName: 'saved_query_id', + oldSubfieldValue: oldSavedQueryId, + newSubfieldValue: newSavedQueryId, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/rule_name_override.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/rule_name_override.ts new file mode 100644 index 0000000000000..805b0dffeb8b1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/rule_name_override.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForRuleNameOverride = ( + oldFieldValue?: DiffableAllFields['rule_name_override'], + newFieldValue?: DiffableAllFields['rule_name_override'] +): SubfieldChange[] => { + const oldFieldName = stringifyToSortedJson(oldFieldValue?.field_name); + const newFieldName = stringifyToSortedJson(newFieldValue?.field_name); + + if (oldFieldName !== newFieldName) { + return [ + { + subfieldName: 'field_name', + oldSubfieldValue: oldFieldName, + newSubfieldValue: newFieldName, + }, + ]; + } + + return []; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/rule_schedule.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/rule_schedule.ts new file mode 100644 index 0000000000000..8bbf0c9235257 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/rule_schedule.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForRuleSchedule = ( + oldFieldValue?: DiffableAllFields['rule_schedule'], + newFieldValue?: DiffableAllFields['rule_schedule'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + if (oldFieldValue?.interval !== newFieldValue?.interval) { + changes.push({ + subfieldName: 'interval', + oldSubfieldValue: stringifyToSortedJson(oldFieldValue?.interval), + newSubfieldValue: stringifyToSortedJson(newFieldValue?.interval), + }); + } + + if (oldFieldValue?.lookback !== newFieldValue?.lookback) { + changes.push({ + subfieldName: 'lookback', + oldSubfieldValue: stringifyToSortedJson(oldFieldValue?.lookback), + newSubfieldValue: stringifyToSortedJson(newFieldValue?.lookback), + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/threat_query.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/threat_query.ts new file mode 100644 index 0000000000000..a9264d1624f5a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/threat_query.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForThreatQuery = ( + oldFieldValue?: DiffableAllFields['threat_query'], + newFieldValue?: DiffableAllFields['threat_query'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldQuery = stringifyToSortedJson(oldFieldValue?.query); + const newQuery = stringifyToSortedJson(newFieldValue?.query); + + if (oldQuery !== newQuery) { + changes.push({ + subfieldName: 'query', + oldSubfieldValue: oldQuery, + newSubfieldValue: newQuery, + }); + } + + const oldLanguage = stringifyToSortedJson(oldFieldValue?.language); + const newLanguage = stringifyToSortedJson(newFieldValue?.language); + + if (oldLanguage !== newLanguage) { + changes.push({ + subfieldName: 'language', + oldSubfieldValue: oldLanguage, + newSubfieldValue: newLanguage, + }); + } + + const oldFilters = stringifyToSortedJson(oldFieldValue?.filters); + const newFilters = stringifyToSortedJson(newFieldValue?.filters); + + if (oldFilters !== newFilters) { + changes.push({ + subfieldName: 'filters', + oldSubfieldValue: oldFilters, + newSubfieldValue: newFilters, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/threshold.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/threshold.ts new file mode 100644 index 0000000000000..5dd07096c0633 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/threshold.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForThreshold = ( + oldFieldValue?: DiffableAllFields['threshold'], + newFieldValue?: DiffableAllFields['threshold'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldField = stringifyToSortedJson(oldFieldValue?.field); + const newField = stringifyToSortedJson(newFieldValue?.field); + + if (oldField !== newField) { + changes.push({ + subfieldName: 'field', + oldSubfieldValue: oldField, + newSubfieldValue: newField, + }); + } + + const oldValue = stringifyToSortedJson(oldFieldValue?.value); + const newValue = stringifyToSortedJson(newFieldValue?.value); + + if (oldValue !== newValue) { + changes.push({ + subfieldName: 'value', + oldSubfieldValue: oldValue, + newSubfieldValue: newValue, + }); + } + + const oldCardinality = stringifyToSortedJson(oldFieldValue?.cardinality); + const newCardinality = stringifyToSortedJson(newFieldValue?.cardinality); + + if (oldCardinality !== newCardinality) { + changes.push({ + subfieldName: 'cardinality', + oldSubfieldValue: oldCardinality, + newSubfieldValue: newCardinality, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/timeline_template.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/timeline_template.ts new file mode 100644 index 0000000000000..502d093374b8d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/timeline_template.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForTimelineTemplate = ( + oldFieldValue?: DiffableAllFields['timeline_template'], + newFieldValue?: DiffableAllFields['timeline_template'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldTimelineId = stringifyToSortedJson(oldFieldValue?.timeline_id); + const newTimelineId = stringifyToSortedJson(newFieldValue?.timeline_id); + + if (oldTimelineId !== newTimelineId) { + changes.push({ + subfieldName: 'timeline_id', + oldSubfieldValue: oldTimelineId, + newSubfieldValue: newTimelineId, + }); + } + + const oldTimelineTitle = stringifyToSortedJson(oldFieldValue?.timeline_title); + const newTimelineTitle = stringifyToSortedJson(newFieldValue?.timeline_title); + + if (oldTimelineTitle !== newTimelineTitle) { + changes.push({ + subfieldName: 'timeline_title', + oldSubfieldValue: oldTimelineTitle, + newSubfieldValue: newTimelineTitle, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/timestamp_override.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/timestamp_override.ts new file mode 100644 index 0000000000000..51d7d0caad64f --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/get_subfield_changes/timestamp_override.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 { stringifyToSortedJson } from '../utils'; +import type { DiffableAllFields } from '../../../../../../../../common/api/detection_engine'; +import type { SubfieldChange } from '../types'; + +export const getSubfieldChangesForTimestampOverride = ( + oldFieldValue?: DiffableAllFields['timestamp_override'], + newFieldValue?: DiffableAllFields['timestamp_override'] +): SubfieldChange[] => { + const changes: SubfieldChange[] = []; + + const oldFieldName = stringifyToSortedJson(oldFieldValue?.field_name); + const newFieldName = stringifyToSortedJson(newFieldValue?.field_name); + + if (oldFieldName !== newFieldName) { + changes.push({ + subfieldName: 'field_name', + oldSubfieldValue: oldFieldName, + newSubfieldValue: newFieldName, + }); + } + + const oldVersionFallbackDisabled = stringifyToSortedJson(oldFieldValue?.fallback_disabled); + const newVersionFallbackDisabled = stringifyToSortedJson(newFieldValue?.fallback_disabled); + + if (oldVersionFallbackDisabled !== newVersionFallbackDisabled) { + changes.push({ + subfieldName: 'fallback_disabled', + oldSubfieldValue: oldVersionFallbackDisabled, + newSubfieldValue: newVersionFallbackDisabled, + }); + } + + return changes; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/constants.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/no_changes.tsx similarity index 52% rename from x-pack/plugins/cloud_security_posture/public/pages/configurations/constants.ts rename to x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/no_changes.tsx index d4a14320fe225..4cd86c69a9ca4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/constants.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/no_changes.tsx @@ -5,6 +5,14 @@ * 2.0. */ -export const FINDINGS_PIT_KEEP_ALIVE = '2m'; -// Set to half of the PIT keep alive to make sure we keep the PIT window open as long as the components are mounted -export const FINDINGS_REFETCH_INTERVAL_MS = 1000 * 60; // One minute +import React from 'react'; +import { EuiText } from '@elastic/eui'; +import * as i18n from './translations'; + +export function NoChanges() { + return ( + <EuiText size="s" color="subdued"> + {i18n.NO_CHANGES} + </EuiText> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield.tsx new file mode 100644 index 0000000000000..d1df52f93c7c7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { DiffableAllFields } from '../../../../../../../common/api/detection_engine'; +import { DiffView } from '../../json_diff/diff_view'; +import { SubfieldHeader } from './subfield_header'; +import { FIELDS_WITH_SUBFIELDS } from './constants'; + +function shouldDisplaySubfieldLabelForField(fieldName: keyof DiffableAllFields): boolean { + return FIELDS_WITH_SUBFIELDS.includes(fieldName); +} + +interface SubfieldProps { + fieldName: keyof DiffableAllFields; + subfieldName: string; + oldSubfieldValue: string; + newSubfieldValue: string; +} + +export const Subfield = ({ + fieldName, + subfieldName, + oldSubfieldValue, + newSubfieldValue, +}: SubfieldProps) => ( + <> + {shouldDisplaySubfieldLabelForField(fieldName) && ( + <SubfieldHeader subfieldName={subfieldName} /> + )} + <DiffView oldSource={oldSubfieldValue} newSource={newSubfieldValue} viewType="unified" /> + </> +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield_changes.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield_changes.tsx new file mode 100644 index 0000000000000..af10edbe1c599 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield_changes.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiHorizontalRule } from '@elastic/eui'; +import type { SubfieldChanges } from './types'; +import { Subfield } from './subfield'; +import type { DiffableAllFields } from '../../../../../../../common/api/detection_engine'; +import { NoChanges } from './no_changes'; + +interface SubfieldChangesProps { + fieldName: keyof DiffableAllFields; + subfieldChanges: SubfieldChanges; +} + +export function SubfieldChanges({ fieldName, subfieldChanges }: SubfieldChangesProps) { + if (subfieldChanges.length === 0) { + return <NoChanges />; + } + + return ( + <> + {subfieldChanges.map((change, index) => { + const shouldShowSeparator = index !== subfieldChanges.length - 1; + + return ( + <> + <Subfield + key={change.subfieldName} + fieldName={fieldName} + subfieldName={change.subfieldName} + oldSubfieldValue={change.oldSubfieldValue} + newSubfieldValue={change.newSubfieldValue} + /> + {shouldShowSeparator ? <EuiHorizontalRule margin="s" size="full" /> : null} + </> + ); + })} + </> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield_header.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield_header.tsx new file mode 100644 index 0000000000000..9eb14188364b2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/subfield_header.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { camelCase, startCase } from 'lodash'; +import { EuiTitle, EuiSpacer } from '@elastic/eui'; +import { fieldToDisplayNameMap } from '../../diff_components/translations'; + +interface SubfieldHeaderProps { + subfieldName: string; +} + +export function SubfieldHeader({ subfieldName }: SubfieldHeaderProps) { + const subfieldLabel = fieldToDisplayNameMap[subfieldName] ?? startCase(camelCase(subfieldName)); + + return ( + <> + <EuiTitle size="xxxs"> + <h4>{subfieldLabel}</h4> + </EuiTitle> + <EuiSpacer size="s" /> + </> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/translations.ts new file mode 100644 index 0000000000000..d60c78646b5ad --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const NO_CHANGES = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.comparisonSide.noChangesLabel', + { + defaultMessage: 'No changes', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/types.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/types.ts new file mode 100644 index 0000000000000..f6e4b7f5d1664 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/types.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface SubfieldChange { + readonly subfieldName: string; + readonly oldSubfieldValue: string; + readonly newSubfieldValue: string; +} + +export type SubfieldChanges = Readonly<SubfieldChange[]>; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/utils.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/utils.ts new file mode 100644 index 0000000000000..fd16366f1a76e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/comparison_side/utils.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import stringify from 'json-stable-stringify'; +import { Version } from '../versions_picker/constants'; +import type { + DiffableAllFields, + ThreeWayDiff, +} from '../../../../../../../common/api/detection_engine'; + +/** + * Picks the field value for a given version either from a three-way diff object or from a user-set resolved value. + * + * @param version - The version for which the field value is to be picked. + * @param fieldThreeWayDiff - The three-way diff object containing the field values for different versions. + * @param resolvedValue - The user-set resolved value resolved value. Used if it is set and the version is final. + * @returns - The field value for the specified version + */ +export function pickFieldValueForVersion<FieldName extends keyof DiffableAllFields>( + version: Version, + fieldThreeWayDiff: ThreeWayDiff<DiffableAllFields[FieldName]>, + resolvedValue?: DiffableAllFields[FieldName] +): DiffableAllFields[FieldName] | undefined { + if (version === Version.Final) { + return resolvedValue ?? fieldThreeWayDiff.merged_version; + } + + const versionFieldToPick = `${version}_version` as const; + return fieldThreeWayDiff[versionFieldToPick]; +} + +/** + * Stringifies a field value to an alphabetically sorted JSON string. + */ +export const stringifyToSortedJson = (fieldValue: unknown): string => { + if (fieldValue === undefined) { + return ''; + } + + if (typeof fieldValue === 'string') { + return fieldValue; + } + + return stringify(fieldValue, { space: 2 }); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/versions_picker/constants.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/versions_picker/constants.ts index ca8b0c75e1727..73a7e89a200a3 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/versions_picker/constants.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/versions_picker/constants.ts @@ -8,6 +8,13 @@ import type { EuiSelectOption } from '@elastic/eui'; import * as i18n from './translations'; +export enum Version { + Base = 'base', + Current = 'current', + Target = 'target', + Final = 'final', +} + export enum SelectedVersions { BaseTarget = 'base_target', BaseCurrent = 'base_current', diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_upgrade_secuirty_packages.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_upgrade_secuirty_packages.test.tsx deleted file mode 100644 index 9454a1c4dfb16..0000000000000 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_upgrade_secuirty_packages.test.tsx +++ /dev/null @@ -1,198 +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 { epmRouteService } from '@kbn/fleet-plugin/common'; -import { renderHook } from '@testing-library/react-hooks'; -import { useKibana, KibanaServices } from '../../../common/lib/kibana'; -import { TestProviders } from '../../../common/mock'; -import { useUpgradeSecurityPackages } from './use_upgrade_security_packages'; - -jest.mock('../../../common/components/user_privileges', () => { - return { - useUserPrivileges: jest.fn().mockReturnValue({ - endpointPrivileges: { - canAccessFleet: true, - }, - }), - }; -}); -jest.mock('../../../common/lib/kibana'); - -const mockGetPrebuiltRulesPackageVersion = - KibanaServices.getPrebuiltRulesPackageVersion as jest.Mock; -const mockGetKibanaVersion = KibanaServices.getKibanaVersion as jest.Mock; -const mockGetKibanaBranch = KibanaServices.getKibanaBranch as jest.Mock; -const mockBuildFlavor = KibanaServices.getBuildFlavor as jest.Mock; -const useKibanaMock = useKibana as jest.MockedFunction<typeof useKibana>; - -describe('When using the `useUpgradeSecurityPackages()` hook', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should call fleet setup first via `isInitialized()` and then send upgrade request', async () => { - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - expect(useKibanaMock().services.fleet?.isInitialized).toHaveBeenCalled(); - expect(useKibanaMock().services.http.post).not.toHaveBeenCalled(); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: '{"packages":["endpoint","security_detection_engine"]}', - }) - ); - }); - - it('should send upgrade request with prerelease:false if in serverless', async () => { - mockGetKibanaVersion.mockReturnValue('8.0.0'); - mockGetKibanaBranch.mockReturnValue('main'); - mockBuildFlavor.mockReturnValue('serverless'); - - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: '{"packages":["endpoint","security_detection_engine"]}', - query: expect.objectContaining({ prerelease: false }), - }) - ); - }); - - it('should send upgrade request with prerelease:false if in serverless SNAPSHOT', async () => { - mockGetKibanaVersion.mockReturnValue('8.0.0-SNAPSHOT'); - mockGetKibanaBranch.mockReturnValue('main'); - mockBuildFlavor.mockReturnValue('serverless'); - - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: '{"packages":["endpoint","security_detection_engine"]}', - query: expect.objectContaining({ prerelease: false }), - }) - ); - }); - - it('should send upgrade request with prerelease:false if build does not include `-SNAPSHOT`', async () => { - mockGetKibanaVersion.mockReturnValue('8.0.0'); - mockGetKibanaBranch.mockReturnValue('release'); - mockBuildFlavor.mockReturnValue('traditional'); - - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: '{"packages":["endpoint","security_detection_engine"]}', - query: expect.objectContaining({ prerelease: false }), - }) - ); - }); - - it('should send upgrade request with prerelease:true if not serverless and branch is `main` AND build includes `-SNAPSHOT`', async () => { - mockGetKibanaVersion.mockReturnValue('8.0.0-SNAPSHOT'); - mockGetKibanaBranch.mockReturnValue('main'); - mockBuildFlavor.mockReturnValue('traditional'); - - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: '{"packages":["endpoint","security_detection_engine"]}', - query: expect.objectContaining({ prerelease: true }), - }) - ); - }); - - it('should send upgrade request with prerelease:true if branch is `release` and build includes `-SNAPSHOT`', async () => { - mockGetKibanaVersion.mockReturnValue('8.0.0-SNAPSHOT'); - mockGetKibanaBranch.mockReturnValue('release'); - mockBuildFlavor.mockReturnValue('traditional'); - - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: '{"packages":["endpoint","security_detection_engine"]}', - query: expect.objectContaining({ prerelease: true }), - }) - ); - }); - - it('should send upgrade request with prerelease:true if branch is `main` and build does not include `-SNAPSHOT`', async () => { - mockGetKibanaVersion.mockReturnValue('8.0.0'); - mockGetKibanaBranch.mockReturnValue('main'); - mockBuildFlavor.mockReturnValue('traditional'); - - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenCalledWith( - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: '{"packages":["endpoint","security_detection_engine"]}', - query: expect.objectContaining({ prerelease: true }), - }) - ); - }); - - it('should send separate upgrade requests if prebuiltRulesPackageVersion is provided', async () => { - mockGetPrebuiltRulesPackageVersion.mockReturnValue('8.2.1'); - - const { waitFor } = renderHook(() => useUpgradeSecurityPackages(), { - wrapper: TestProviders, - }); - - await waitFor(() => (useKibanaMock().services.http.post as jest.Mock).mock.calls.length > 0); - - expect(useKibanaMock().services.http.post).toHaveBeenNthCalledWith( - 1, - `${epmRouteService.getInstallPath('security_detection_engine', '8.2.1')}`, - expect.objectContaining({ query: { prerelease: true } }) - ); - expect(useKibanaMock().services.http.post).toHaveBeenNthCalledWith( - 2, - `${epmRouteService.getBulkInstallPath()}`, - expect.objectContaining({ - body: expect.stringContaining('endpoint'), - query: expect.objectContaining({ prerelease: true }), - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_upgrade_security_packages.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_upgrade_security_packages.ts index 8ad266169231d..e19cf2bcacf94 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_upgrade_security_packages.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/logic/use_upgrade_security_packages.ts @@ -7,88 +7,29 @@ import { useIsMutating } from '@tanstack/react-query'; import { useEffect } from 'react'; -import { PREBUILT_RULES_PACKAGE_NAME } from '../../../../common/detection_engine/constants'; -import { useUserPrivileges } from '../../../common/components/user_privileges'; -import { KibanaServices, useKibana } from '../../../common/lib/kibana'; -import type { BulkInstallFleetPackagesProps, InstallFleetPackageProps } from '../api/api'; import { - BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY, - useBulkInstallFleetPackagesMutation, -} from '../api/hooks/use_bulk_install_fleet_packages_mutation'; -import { - INSTALL_FLEET_PACKAGE_MUTATION_KEY, - useInstallFleetPackageMutation, -} from '../api/hooks/use_install_fleet_package_mutation'; + BOOTSTRAP_PREBUILT_RULES_KEY, + useBootstrapPrebuiltRulesMutation, +} from '../api/hooks/use_bootstrap_prebuilt_rules'; /** * Install or upgrade the security packages (endpoint and prebuilt rules) */ export const useUpgradeSecurityPackages = () => { - const context = useKibana(); - const canAccessFleet = useUserPrivileges().endpointPrivileges.canAccessFleet; - const { mutate: bulkInstallFleetPackages } = useBulkInstallFleetPackagesMutation(); - const { mutate: installFleetPackage } = useInstallFleetPackageMutation(); + const { mutate: bootstrapPrebuiltRules } = useBootstrapPrebuiltRulesMutation(); useEffect(() => { - if (!canAccessFleet) { - return; - } - - (async () => { - // Make sure fleet is initialized first - await context.services.fleet?.isInitialized(); - - // Install the latest prerelease if in non-production non-serverless environments - const prerelease = - KibanaServices.getBuildFlavor() === 'traditional' && - (KibanaServices.getKibanaVersion().includes('-SNAPSHOT') || - KibanaServices.getKibanaBranch() === 'main'); - - const prebuiltRulesPackageVersion = KibanaServices.getPrebuiltRulesPackageVersion(); - // ignore the response for now since we aren't notifying the user - const packages = ['endpoint', PREBUILT_RULES_PACKAGE_NAME]; - - // If `prebuiltRulesPackageVersion` is provided, try to install that version - // Must be done as two separate requests as bulk API doesn't support versions - if (prebuiltRulesPackageVersion != null) { - installFleetPackage({ - packageName: PREBUILT_RULES_PACKAGE_NAME, - packageVersion: prebuiltRulesPackageVersion, - prerelease, - force: true, - }); - packages.splice(packages.indexOf(PREBUILT_RULES_PACKAGE_NAME), 1); - } - - // Note: if `prerelease:true` option is provided, endpoint package will also be installed as prerelease - bulkInstallFleetPackages({ - packages, - prerelease, - }); - })(); - }, [bulkInstallFleetPackages, canAccessFleet, context.services.fleet, installFleetPackage]); + bootstrapPrebuiltRules(); + }, [bootstrapPrebuiltRules]); }; /** * @returns true if the security packages are being installed or upgraded */ export const useIsUpgradingSecurityPackages = () => { - const isInstallingPackages = useIsMutating({ - predicate: ({ options: { mutationKey }, state: { variables } }) => { - // The mutation is bulk Fleet packages installation. Check if the packages include the prebuilt rules package - if (mutationKey === BULK_INSTALL_FLEET_PACKAGES_MUTATION_KEY) { - return (variables as BulkInstallFleetPackagesProps).packages.includes( - PREBUILT_RULES_PACKAGE_NAME - ); - } - - // The mutation is single Fleet package installation. Check if the package is the prebuilt rules package - if (mutationKey === INSTALL_FLEET_PACKAGE_MUTATION_KEY) { - return (variables as InstallFleetPackageProps).packageName === PREBUILT_RULES_PACKAGE_NAME; - } - return false; - }, + const bootstrappingRules = useIsMutating({ + mutationKey: BOOTSTRAP_PREBUILT_RULES_KEY, }); - return isInstallingPackages > 0; + return bootstrappingRules > 0; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.test.tsx deleted file mode 100644 index 3740c7b2ef7df..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.test.tsx +++ /dev/null @@ -1,218 +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, mount } from 'enzyme'; - -import { AlertsCount } from './alerts_count'; -import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; -import { TestProviders } from '../../../../common/mock'; -import { DragDropContextWrapper } from '../../../../common/components/drag_and_drop/drag_drop_context_wrapper'; -import { mockBrowserFields } from '../../../../common/containers/source/mock'; -import type { AlertsCountAggregation } from './types'; -import { emptyStackByField0Response } from './mocks/mock_response_empty_field0'; -import { - buckets as oneGroupByResponseBuckets, - mockMultiGroupResponse, -} from './mocks/mock_response_multi_group'; -import { - buckets as twoGroupByResponseBuckets, - singleGroupResponse, -} from './mocks/mock_response_single_group'; - -jest.mock('../../../../common/lib/kibana'); -const mockDispatch = jest.fn(); - -jest.mock('react-router-dom', () => { - const actual = jest.requireActual('react-router-dom'); - return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; -}); - -jest.mock('react-redux', () => { - const original = jest.requireActual('react-redux'); - return { - ...original, - useDispatch: () => mockDispatch, - }; -}); - -describe('AlertsCount', () => { - it('renders correctly', () => { - const wrapper = shallow( - <AlertsCount - data={{} as AlertSearchResponse<{}, AlertsCountAggregation>} - loading={false} - stackByField0={'test_selected_field'} - stackByField1={undefined} - /> - ); - - expect(wrapper.find('[data-test-subj="alertsCountTable"]').exists()).toBeTruthy(); - }); - - it('renders the expected table body message when stackByField0 is an empty string', () => { - const wrapper = mount( - <TestProviders> - <AlertsCount - data={emptyStackByField0Response} - loading={false} - stackByField0={''} - stackByField1={undefined} - /> - </TestProviders> - ); - - expect(wrapper.find('[data-test-subj="alertsCountTable"] tbody').text()).toEqual( - 'No items found' - ); - }); - - describe('one group by field', () => { - oneGroupByResponseBuckets.forEach((bucket, i) => { - it(`renders the expected stackByField0 column text for bucket '${bucket.key}'`, () => { - const wrapper = mount( - <TestProviders> - <DragDropContextWrapper browserFields={mockBrowserFields}> - <AlertsCount - data={mockMultiGroupResponse} - loading={false} - stackByField0={'kibana.alert.rule.name'} - stackByField1={undefined} - /> - </DragDropContextWrapper> - </TestProviders> - ); - - expect( - wrapper - .find(`[data-test-subj="stackByField0Key"] div.euiTableCellContent`) - .hostNodes() - .at(i) - .text() - ).toEqual(bucket.key); - }); - }); - - oneGroupByResponseBuckets.forEach((bucket, i) => { - it(`renders the expected doc_count column value for bucket '${bucket.key}'`, () => { - const wrapper = mount( - <TestProviders> - <DragDropContextWrapper browserFields={mockBrowserFields}> - <AlertsCount - data={mockMultiGroupResponse} - loading={false} - stackByField0={'test_selected_field'} - stackByField1={undefined} - /> - </DragDropContextWrapper> - </TestProviders> - ); - - expect( - wrapper - .find(`[data-test-subj="doc_count"] div.euiTableCellContent`) - .hostNodes() - .at(i) - .text() - ).toEqual(`${bucket.doc_count}`); - }); - }); - }); - - describe('two group by fields: stackByField0 column', () => { - let resultRow = 0; - - twoGroupByResponseBuckets.forEach((bucket) => { - bucket.stackByField1.buckets.forEach((b) => { - it(`renders the expected stackByField0 column text for stackByField0: '${bucket.key}', stackByField1 '${b.key}'`, () => { - const wrapper = mount( - <TestProviders> - <DragDropContextWrapper browserFields={mockBrowserFields}> - <AlertsCount - data={singleGroupResponse} - loading={false} - stackByField0={'kibana.alert.rule.name'} - stackByField1={'host.name'} - /> - </DragDropContextWrapper> - </TestProviders> - ); - - expect( - wrapper - .find(`[data-test-subj="stackByField0Key"] div.euiTableCellContent`) - .hostNodes() - .at(resultRow++) - .text() - ).toEqual(bucket.key); - }); - }); - }); - }); - - describe('two group by fields: stackByField1 column', () => { - let resultRow = 0; - - twoGroupByResponseBuckets.forEach((bucket) => { - bucket.stackByField1.buckets.forEach((b, i) => { - it(`renders the expected stackByField1 column text for stackByField0: '${bucket.key}', stackByField1 '${b.key}'`, () => { - const wrapper = mount( - <TestProviders> - <DragDropContextWrapper browserFields={mockBrowserFields}> - <AlertsCount - data={singleGroupResponse} - loading={false} - stackByField0={'kibana.alert.rule.name'} - stackByField1={'host.name'} - /> - </DragDropContextWrapper> - </TestProviders> - ); - - expect( - wrapper - .find(`[data-test-subj="stackByField1Key"] div.euiTableCellContent`) - .hostNodes() - .at(resultRow++) - .text() - ).toEqual(b.key); - }); - }); - }); - }); - - describe('two group by fields: stackByField1DocCount column', () => { - let resultRow = 0; - - twoGroupByResponseBuckets.forEach((bucket) => { - bucket.stackByField1.buckets.forEach((b, i) => { - it(`renders the expected doc_count column value for stackByField0: '${bucket.key}', stackByField1 '${b.key}'`, () => { - const wrapper = mount( - <TestProviders> - <DragDropContextWrapper browserFields={mockBrowserFields}> - <AlertsCount - data={singleGroupResponse} - loading={false} - stackByField0={'kibana.alert.rule.name'} - stackByField1={'host.name'} - /> - </DragDropContextWrapper> - </TestProviders> - ); - - expect( - wrapper - .find(`[data-test-subj="stackByField1DocCount"] div.euiTableCellContent`) - .hostNodes() - .at(resultRow++) - .text() - ).toEqual(`${b.doc_count}`); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx deleted file mode 100644 index c83650b8e15d8..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx +++ /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 { EuiInMemoryTable } from '@elastic/eui'; -import { isEmpty } from 'lodash/fp'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; - -import { useUiSetting$ } from '../../../../common/lib/kibana'; -import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; -import type { AlertsCountAggregation } from './types'; -import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; -import { - getMaxRiskSubAggregations, - getUpToMaxBuckets, -} from '../alerts_treemap_panel/alerts_treemap/lib/helpers'; -import { getFlattenedBuckets } from '../alerts_treemap_panel/alerts_treemap/lib/flatten/get_flattened_buckets'; -import type { FlattenedBucket, RawBucket } from '../alerts_treemap_panel/alerts_treemap/types'; -import { - getMultiGroupAlertsCountTableColumns, - getSingleGroupByAlertsCountTableColumns, -} from './columns'; -import { DEFAULT_STACK_BY_FIELD0_SIZE } from './helpers'; - -interface AlertsCountProps { - loading: boolean; - data: AlertSearchResponse<unknown, AlertsCountAggregation>; - stackByField0: string; - stackByField1: string | undefined; -} - -const Wrapper = styled.div` - margin-top: -${({ theme }) => theme.eui.euiSizeS}; -`; - -export const AlertsCountComponent: React.FC<AlertsCountProps> = ({ - data, - loading, - stackByField0, - stackByField1, -}) => { - const [defaultNumberFormat] = useUiSetting$<string>(DEFAULT_NUMBER_FORMAT); - - const tableColumns = useMemo( - () => - isEmpty(stackByField1?.trim()) - ? getSingleGroupByAlertsCountTableColumns({ - defaultNumberFormat, - stackByField0, - }) - : getMultiGroupAlertsCountTableColumns({ - defaultNumberFormat, - stackByField0, - stackByField1, - }), - [defaultNumberFormat, stackByField0, stackByField1] - ); - - const buckets: RawBucket[] = useMemo( - () => - getUpToMaxBuckets({ - buckets: data.aggregations?.stackByField0?.buckets, - maxItems: DEFAULT_STACK_BY_FIELD0_SIZE, - }), - [data.aggregations?.stackByField0?.buckets] - ); - - const maxRiskSubAggregations = useMemo(() => getMaxRiskSubAggregations(buckets), [buckets]); - - const items: FlattenedBucket[] = useMemo( - () => - isEmpty(stackByField1?.trim()) - ? buckets - : getFlattenedBuckets({ - buckets, - maxRiskSubAggregations, - stackByField0, - }), - [buckets, maxRiskSubAggregations, stackByField0, stackByField1] - ); - - return ( - <Wrapper data-test-subj="alertsCountTable" className="eui-yScroll"> - <EuiInMemoryTable columns={tableColumns} items={items} loading={loading} sorting={true} /> - </Wrapper> - ); -}; - -AlertsCountComponent.displayName = 'AlertsCountComponent'; - -export const AlertsCount = React.memo(AlertsCountComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.test.tsx deleted file mode 100644 index c5600fe7eda94..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.test.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { omit } from 'lodash/fp'; -import { - getMultiGroupAlertsCountTableColumns, - getSingleGroupByAlertsCountTableColumns, -} from './columns'; - -describe('columns', () => { - const defaultNumberFormat = '0,0.[000]'; - const stackByField0 = 'kibana.alert.rule.name'; - - describe('getMultiGroupAlertsCountTableColumns', () => { - const stackByField1 = 'host.name'; - - test('it returns the expected columns', () => { - expect( - getMultiGroupAlertsCountTableColumns({ - defaultNumberFormat, - stackByField0, - stackByField1, - }).map((x) => omit('render', x)) - ).toEqual([ - { - 'data-test-subj': 'stackByField0Key', - field: 'key', - name: 'Top 1000 values of kibana.alert.rule.name', - truncateText: false, - }, - { - 'data-test-subj': 'stackByField1Key', - field: 'stackByField1Key', - name: 'Top 1000 values of host.name', - truncateText: false, - }, - { - 'data-test-subj': 'stackByField1DocCount', - dataType: 'number', - field: 'stackByField1DocCount', - name: 'Count of records', - sortable: true, - textOnly: true, - }, - ]); - }); - }); - - describe('getSingleGroupByAlertsCountTableColumns', () => { - test('it returns the expected columns', () => { - expect( - getSingleGroupByAlertsCountTableColumns({ defaultNumberFormat, stackByField0 }).map((x) => - omit('render', x) - ) - ).toEqual([ - { - 'data-test-subj': 'stackByField0Key', - field: 'key', - name: 'kibana.alert.rule.name', - truncateText: false, - }, - { - 'data-test-subj': 'doc_count', - dataType: 'number', - field: 'doc_count', - name: 'Count of records', - sortable: true, - textOnly: true, - }, - ]); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.tsx deleted file mode 100644 index 9b9e3081d2fe5..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.tsx +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import type { EuiBasicTableColumn } from '@elastic/eui'; -import numeral from '@elastic/numeral'; - -import { TableId } from '@kbn/securitysolution-data-table'; -import type { FlattenedBucket } from '../alerts_treemap_panel/alerts_treemap/types'; -import { DefaultDraggable } from '../../../../common/components/draggables'; -import type { GenericBuckets } from '../../../../../common/search_strategy/common'; -import * as i18n from './translations'; -import { DEFAULT_STACK_BY_FIELD0_SIZE, DEFAULT_STACK_BY_FIELD1_SIZE } from './helpers'; - -export const getSingleGroupByAlertsCountTableColumns = ({ - defaultNumberFormat, - stackByField0, -}: { - defaultNumberFormat: string; - stackByField0: string; -}): Array<EuiBasicTableColumn<GenericBuckets>> => [ - { - 'data-test-subj': 'stackByField0Key', - field: 'key', - name: stackByField0, - render: function DraggableStackOptionField(value: string) { - return ( - <DefaultDraggable - isDraggable={false} - field={stackByField0} - hideTopN={true} - id={`alert-count-draggable-stackByField0-${stackByField0}-${value}`} - value={value} - tooltipContent={null} - scopeId={TableId.alertsOnAlertsPage} - /> - ); - }, - truncateText: false, - }, - { - 'data-test-subj': 'doc_count', - dataType: 'number', - field: 'doc_count', - name: i18n.COUNT_TABLE_COLUMN_TITLE, - render: (item: string) => numeral(item).format(defaultNumberFormat), - sortable: true, - textOnly: true, - }, -]; - -export const getMultiGroupAlertsCountTableColumns = ({ - defaultNumberFormat, - stackByField0, - stackByField1, -}: { - defaultNumberFormat: string; - stackByField0: string; - stackByField1: string | undefined; -}): Array<EuiBasicTableColumn<FlattenedBucket>> => [ - { - 'data-test-subj': 'stackByField0Key', - field: 'key', - name: i18n.COLUMN_LABEL({ fieldName: stackByField0, topN: DEFAULT_STACK_BY_FIELD0_SIZE }), - render: function DraggableStackOptionField(value: string) { - return ( - <DefaultDraggable - isDraggable={false} - field={stackByField0} - hideTopN={true} - id={`alert-count-draggable-stackByField0-${stackByField0}-${stackByField1}-${value}`} - value={value} - tooltipContent={null} - scopeId={TableId.alertsOnAlertsPage} - /> - ); - }, - truncateText: false, - }, - { - 'data-test-subj': 'stackByField1Key', - field: 'stackByField1Key', - name: i18n.COLUMN_LABEL({ fieldName: stackByField1 ?? '', topN: DEFAULT_STACK_BY_FIELD1_SIZE }), - render: function DraggableStackOptionField(value: string) { - return ( - <DefaultDraggable - isDraggable={false} - field={stackByField1 ?? ''} - hideTopN={true} - id={`alert-count-draggable-stackByField1-${stackByField0}-${stackByField1}-${value}`} - value={value} - tooltipContent={null} - scopeId={TableId.alertsOnAlertsPage} - /> - ); - }, - truncateText: false, - }, - { - 'data-test-subj': 'stackByField1DocCount', - dataType: 'number', - field: 'stackByField1DocCount', - name: i18n.COUNT_TABLE_COLUMN_TITLE, - render: (item: string) => numeral(item).format(defaultNumberFormat), - sortable: true, - textOnly: true, - }, -]; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.test.tsx deleted file mode 100644 index e651f17d59157..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.test.tsx +++ /dev/null @@ -1,132 +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 { getAlertsCountQuery } from './helpers'; - -const stackByField0 = 'kibana.alert.rule.name'; -const stackByField1 = 'host.name'; -const from = '2022-07-08T06:00:00.000Z'; -const to = '2022-07-09T05:59:59.999Z'; -const additionalFilters = [ - { - bool: { - must: [], - filter: [ - { - term: { - 'kibana.alert.workflow_status': 'open', - }, - }, - ], - should: [], - must_not: [ - { - exists: { - field: 'kibana.alert.building_block_type', - }, - }, - ], - }, - }, -]; -const runtimeMappings = {}; - -describe('helpers', () => { - describe('getAlertsCountQuery', () => { - test('it returns the expected query when stackByField1 is specified', () => { - expect( - getAlertsCountQuery({ - additionalFilters, - from, - runtimeMappings, - stackByField0, - stackByField1, - to, - }) - ).toEqual({ - size: 0, - aggs: { - stackByField0: { - terms: { field: 'kibana.alert.rule.name', order: { _count: 'desc' }, size: 1000 }, - aggs: { - stackByField1: { - terms: { field: 'host.name', order: { _count: 'desc' }, size: 1000 }, - }, - }, - }, - }, - query: { - bool: { - filter: [ - { - bool: { - must: [], - filter: [{ term: { 'kibana.alert.workflow_status': 'open' } }], - should: [], - must_not: [{ exists: { field: 'kibana.alert.building_block_type' } }], - }, - }, - { - range: { - '@timestamp': { - gte: '2022-07-08T06:00:00.000Z', - lte: '2022-07-09T05:59:59.999Z', - }, - }, - }, - ], - }, - }, - runtime_mappings: {}, - }); - }); - - test('it returns the expected query when stackByField1 is `undefined`', () => { - expect( - getAlertsCountQuery({ - additionalFilters, - from, - runtimeMappings, - stackByField0, - stackByField1: undefined, - to, - }) - ).toEqual({ - size: 0, - aggs: { - stackByField0: { - terms: { field: 'kibana.alert.rule.name', order: { _count: 'desc' }, size: 1000 }, - aggs: {}, - }, - }, - query: { - bool: { - filter: [ - { - bool: { - must: [], - filter: [{ term: { 'kibana.alert.workflow_status': 'open' } }], - should: [], - must_not: [{ exists: { field: 'kibana.alert.building_block_type' } }], - }, - }, - { - range: { - '@timestamp': { - gte: '2022-07-08T06:00:00.000Z', - lte: '2022-07-09T05:59:59.999Z', - }, - }, - }, - ], - }, - }, - runtime_mappings: {}, - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.tsx deleted file mode 100644 index 1537d1f1fd212..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { getOptionalSubAggregation } from '../alerts_treemap_panel/alerts_treemap/query'; - -export const DEFAULT_STACK_BY_FIELD0_SIZE = 1000; -export const DEFAULT_STACK_BY_FIELD1_SIZE = 1000; - -export const getAlertsCountQuery = ({ - additionalFilters = [], - from, - runtimeMappings, - stackByField0, - stackByField1, - to, -}: { - additionalFilters: Array<{ - bool: { filter: unknown[]; should: unknown[]; must_not: unknown[]; must: unknown[] }; - }>; - from: string; - runtimeMappings?: MappingRuntimeFields; - stackByField0: string; - stackByField1: string | undefined; - to: string; -}) => { - return { - size: 0, - aggs: { - stackByField0: { - terms: { - field: stackByField0, - order: { - _count: 'desc', - }, - size: DEFAULT_STACK_BY_FIELD0_SIZE, - }, - aggs: { - ...getOptionalSubAggregation({ - stackByField1, - stackByField1Size: DEFAULT_STACK_BY_FIELD1_SIZE, - }), - }, - }, - }, - query: { - bool: { - filter: [ - ...additionalFilters, - { - range: { - '@timestamp': { - gte: from, - lte: to, - }, - }, - }, - ], - }, - }, - runtime_mappings: runtimeMappings, - }; -}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx index 18ba80752c398..a9d9821601d25 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx @@ -9,13 +9,10 @@ import type { EuiComboBox } from '@elastic/eui'; import type { Action } from '@kbn/ui-actions-plugin/public'; import React, { memo, useMemo } from 'react'; import { v4 as uuidv4 } from 'uuid'; - import type { Filter } from '@kbn/es-query'; import { useGlobalTime } from '../../../../common/containers/use_global_time'; import { HeaderSection } from '../../../../common/components/header_section'; - import { InspectButtonContainer } from '../../../../common/components/inspect'; - import * as i18n from './translations'; import { KpiPanel } from '../common/components'; import { FieldSelection } from '../../../../common/components/field_selection'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_empty_field0.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_empty_field0.ts deleted file mode 100644 index a2d9d92cd75a6..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_empty_field0.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 type { AlertSearchResponse } from '../../../../containers/detection_engine/alerts/types'; -import type { AlertsCountAggregation } from '../types'; - -export const emptyStackByField0Response: AlertSearchResponse<unknown, AlertsCountAggregation> = { - took: 0, - timeout: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 87, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - aggregations: { - stackByField0: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, - }, -}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_multi_group.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_multi_group.ts deleted file mode 100644 index 730fded03f88b..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_multi_group.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { AlertSearchResponse } from '../../../../containers/detection_engine/alerts/types'; -import type { AlertsCountAggregation } from '../types'; - -export const buckets = [ - { - key: 'matches everything', - doc_count: 34, - }, - { - key: 'EQL process sequence', - doc_count: 28, - }, - { - key: 'Endpoint Security', - doc_count: 19, - }, - { - key: 'mimikatz process started', - doc_count: 5, - }, - { - key: 'Threshold rule', - doc_count: 1, - }, -]; - -/** - * A mock response to a request containing multiple group by fields - */ -export const mockMultiGroupResponse: AlertSearchResponse<unknown, AlertsCountAggregation> = { - took: 0, - timeout: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 87, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - aggregations: { - stackByField0: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets, - }, - }, -}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_single_group.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_single_group.ts deleted file mode 100644 index e7c0f982be03b..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_single_group.ts +++ /dev/null @@ -1,146 +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 { AlertSearchResponse } from '../../../../containers/detection_engine/alerts/types'; -import type { AlertsCountAggregation } from '../types'; - -export const buckets = [ - { - key: 'matches everything', - doc_count: 34, - stackByField1: { - buckets: [ - { - key: 'Host-k8iyfzraq9', - doc_count: 12, - }, - { - key: 'Host-ao1a4wu7vn', - doc_count: 10, - }, - { - key: 'Host-3fbljiq8rj', - doc_count: 7, - }, - { - key: 'Host-r4y6xi92ob', - doc_count: 5, - }, - ], - }, - }, - { - key: 'EQL process sequence', - doc_count: 28, - stackByField1: { - sum_other_doc_count: 0, - buckets: [ - { - key: 'Host-k8iyfzraq9', - doc_count: 10, - }, - { - key: 'Host-ao1a4wu7vn', - doc_count: 7, - }, - { - key: 'Host-3fbljiq8rj', - doc_count: 5, - }, - { - key: 'Host-r4y6xi92ob', - doc_count: 3, - }, - ], - }, - }, - { - key: 'Endpoint Security', - doc_count: 19, - stackByField1: { - sum_other_doc_count: 0, - buckets: [ - { - key: 'Host-ao1a4wu7vn', - doc_count: 11, - }, - { - key: 'Host-3fbljiq8rj', - doc_count: 6, - }, - { - key: 'Host-k8iyfzraq9', - doc_count: 1, - }, - { - key: 'Host-r4y6xi92ob', - doc_count: 1, - }, - ], - }, - }, - { - key: 'mimikatz process started', - doc_count: 5, - stackByField1: { - sum_other_doc_count: 0, - buckets: [ - { - key: 'Host-k8iyfzraq9', - doc_count: 3, - }, - { - key: 'Host-3fbljiq8rj', - doc_count: 1, - }, - { - key: 'Host-r4y6xi92ob', - doc_count: 1, - }, - ], - }, - }, - { - key: 'Threshold rule', - doc_count: 1, - stackByField1: { - sum_other_doc_count: 0, - buckets: [ - { - key: 'Host-r4y6xi92ob', - doc_count: 1, - }, - ], - }, - }, -]; - -export const singleGroupResponse: AlertSearchResponse<unknown, AlertsCountAggregation> = { - took: 0, - timeout: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 87, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - aggregations: { - stackByField0: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets, - }, - }, -}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.test.tsx deleted file mode 100644 index 4b64a214bd02b..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { mount, shallow } from 'enzyme'; - -import { AlertsHistogram } from './alerts_histogram'; -import { TestProviders } from '../../../../common/mock'; - -jest.mock('../../../../common/lib/kibana'); - -const legendItems = [ - { - color: '#1EA593', - count: 77, - dataProviderId: - 'draggable-legend-item-2f890398-548e-4604-b2de-525f0eecd124-kibana_alert_rule_name-matches everything', - field: 'kibana.alert.rule.name', - value: 'matches everything', - }, - { - color: '#2B70F7', - count: 56, - dataProviderId: - 'draggable-legend-item-07aca01b-d334-424d-98c0-6d6bc9f8a886-kibana_alert_rule_name-Endpoint Security', - field: 'kibana.alert.rule.name', - value: 'Endpoint Security', - }, -]; - -const defaultProps = { - legendItems, - loading: false, - data: [], - from: '2020-07-07T08:20:18.966Z', - to: '2020-07-08T08:20:18.966Z', - updateDateRange: jest.fn(), -}; - -describe('AlertsHistogram', () => { - it('renders correctly', () => { - const wrapper = shallow(<AlertsHistogram {...defaultProps} />); - - expect(wrapper.find('Chart').exists()).toBeTruthy(); - }); - - it('renders a legend with the default width', () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogram {...defaultProps} /> - </TestProviders> - ); - - expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( - 'min-width', - '165px' - ); - }); - - it('renders a legend with the specified `legendWidth`', () => { - const legendMinWidth = 1234; - - const wrapper = mount( - <TestProviders> - <AlertsHistogram {...defaultProps} legendMinWidth={legendMinWidth} /> - </TestProviders> - ); - - expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( - 'min-width', - `${legendMinWidth}px` - ); - }); -}); 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 deleted file mode 100644 index 83052e4e5a2f1..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx +++ /dev/null @@ -1,127 +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 { ChartSizeArray } from '@elastic/charts'; -import { i18n } from '@kbn/i18n'; -import { - Axis, - Chart, - HistogramBarSeries, - Position, - Settings, - ScaleType, - LegendValue, -} from '@elastic/charts'; -import { EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; -import React, { useMemo } from 'react'; - -import type { UpdateDateRange, ChartData } from '../../../../common/components/charts/common'; -import { useThemes } from '../../../../common/components/charts/common'; -import { histogramDateTimeFormatter } from '../../../../common/components/utils'; -import { hasValueToDisplay } from '../../../../common/utils/validators'; -import { DraggableLegend } from '../../../../common/components/charts/draggable_legend'; -import type { LegendItem } from '../../../../common/components/charts/draggable_legend_item'; -import { EMPTY_VALUE_LABEL } from '../../../../common/components/charts/translation'; - -import type { HistogramData } from './types'; - -const DEFAULT_CHART_HEIGHT = 174; - -interface AlertsHistogramProps { - chartHeight?: number; - from: string; - legendItems: LegendItem[]; - legendPosition?: Position; - legendMinWidth?: number; - loading: boolean; - showLegend?: boolean; - to: string; - data: HistogramData[]; - updateDateRange: UpdateDateRange; -} -export const AlertsHistogram = React.memo<AlertsHistogramProps>( - ({ - chartHeight = DEFAULT_CHART_HEIGHT, - data, - from, - legendItems, - legendPosition = Position.Right, - legendMinWidth, - loading, - showLegend, - to, - updateDateRange, - }) => { - const { baseTheme, theme } = useThemes(); - const chartSize: ChartSizeArray = useMemo(() => ['100%', chartHeight], [chartHeight]); - const xAxisId = 'alertsHistogramAxisX'; - const yAxisId = 'alertsHistogramAxisY'; - const id = 'alertsHistogram'; - const yAccessors = useMemo(() => ['y'], []); - const splitSeriesAccessors = useMemo( - () => [(datum: ChartData) => (hasValueToDisplay(datum.g) ? datum.g : EMPTY_VALUE_LABEL)], - [] - ); - const tickFormat = useMemo(() => histogramDateTimeFormatter([from, to]), [from, to]); - - return ( - <> - {loading && ( - <EuiProgress - data-test-subj="loadingPanelAlertsHistogram" - size="xs" - position="absolute" - color="accent" - /> - )} - - <EuiFlexGroup gutterSize="none"> - <EuiFlexItem grow={true}> - <Chart size={chartSize}> - <Settings - legendPosition={legendPosition} - onBrushEnd={updateDateRange} - // showLegend controls the default legend coming from @elastic/charts, we show them when our customized legend items don't exist (but we still want to show legend). - showLegend={showLegend && legendItems.length === 0} - legendValues={showLegend ? [LegendValue.CurrentAndLastValue] : []} - theme={theme} - baseTheme={baseTheme} - locale={i18n.getLocale()} - /> - - <Axis id={xAxisId} position={Position.Bottom} tickFormat={tickFormat} /> - - <Axis id={yAxisId} position={Position.Left} /> - - <HistogramBarSeries - id={id} - xScaleType={ScaleType.Time} - yScaleType={ScaleType.Linear} - xAccessor="x" - yAccessors={yAccessors} - stackAccessors={['true']} - splitSeriesAccessors={splitSeriesAccessors} - data={data} - /> - </Chart> - </EuiFlexItem> - <EuiFlexItem grow={false}> - {legendItems.length > 0 && ( - <DraggableLegend - legendItems={legendItems} - height={chartHeight} - minWidth={legendMinWidth} - /> - )} - </EuiFlexItem> - </EuiFlexGroup> - </> - ); - } -); - -AlertsHistogram.displayName = 'AlertsHistogram'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx index 519a7975c5891..2599b10b93a48 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.test.tsx @@ -7,12 +7,7 @@ import type { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import type { Embeddable } from '@kbn/embeddable-plugin/public'; -import { - createResetGroupByFieldAction, - formatAlertsData, - showInitialLoadingSpinner, -} from './helpers'; -import { result, textResult, stackedByBooleanField, stackedByTextField } from './mock_data'; +import { createResetGroupByFieldAction, showInitialLoadingSpinner } from './helpers'; import type { LensDataTableEmbeddable } from '../../../../common/components/visualization_actions/types'; describe('helpers', () => { @@ -43,18 +38,6 @@ describe('helpers', () => { }); }); -describe('formatAlertsData', () => { - test('stack by a boolean field', () => { - const res = formatAlertsData(stackedByBooleanField); - expect(res).toEqual(result); - }); - - test('stack by a text field', () => { - const res = formatAlertsData(stackedByTextField); - expect(res).toEqual(textResult); - }); -}); - describe('createResetGroupByFieldAction', () => { let action: Action; const embeddable = { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx index cbeb7e9c3d0a5..8757316038b4a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/helpers.tsx @@ -5,95 +5,11 @@ * 2.0. */ -import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { isEmpty } from 'lodash/fp'; -import moment from 'moment'; - import type { Action, ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import type { Embeddable } from '@kbn/embeddable-plugin/public'; -import type { HistogramData, AlertsAggregation, AlertsBucket, AlertsGroupBucket } from './types'; -import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; import { RESET_GROUP_BY_FIELDS } from '../../../../common/components/chart_settings_popover/configurations/default/translations'; import type { LensDataTableEmbeddable } from '../../../../common/components/visualization_actions/types'; -const EMPTY_ALERTS_DATA: HistogramData[] = []; - -export const formatAlertsData = (alertsData: AlertSearchResponse<{}, AlertsAggregation> | null) => { - const groupBuckets: AlertsGroupBucket[] = - alertsData?.aggregations?.alertsByGrouping?.buckets ?? []; - return groupBuckets.reduce<HistogramData[]>( - (acc, { key_as_string: keyAsString, key: group, alerts }) => { - const alertsBucket: AlertsBucket[] = alerts.buckets ?? []; - - return [ - ...acc, - // eslint-disable-next-line @typescript-eslint/naming-convention - ...alertsBucket.map(({ key, doc_count }: AlertsBucket) => ({ - x: key, - y: doc_count, - g: keyAsString ?? group.toString(), - })), - ]; - }, - EMPTY_ALERTS_DATA - ); -}; - -export const getAlertsHistogramQuery = ( - stackByField: string, - from: string, - to: string, - additionalFilters: Array<{ - bool: { filter: unknown[]; should: unknown[]; must_not: unknown[]; must: unknown[] }; - }>, - runtimeMappings?: MappingRuntimeFields -) => { - return { - aggs: { - alertsByGrouping: { - terms: { - field: stackByField, - order: { - _count: 'desc', - }, - size: 10, - }, - aggs: { - alerts: { - date_histogram: { - field: '@timestamp', - fixed_interval: `${Math.floor(moment(to).diff(moment(from)) / 32)}ms`, - min_doc_count: 0, - extended_bounds: { - min: from, - max: to, - }, - }, - }, - }, - }, - }, - query: { - bool: { - filter: [ - ...additionalFilters, - { - range: { - '@timestamp': { - gte: from, - lte: to, - }, - }, - }, - ], - }, - }, - runtime_mappings: runtimeMappings, - _source: false, - size: 0, - }; -}; - /** * Returns `true` when the alerts histogram initial loading spinner should be shown * @@ -108,22 +24,6 @@ export const showInitialLoadingSpinner = ({ isLoadingAlerts: boolean; }): boolean => isInitialLoading && isLoadingAlerts; -export const parseFilterQuery = (query?: string) => { - try { - return query != null && !isEmpty(query) ? JSON.parse(query) : {}; - } catch { - return {}; - } -}; - -export const buildFilterQuery = (query?: string) => { - try { - return isEmpty(query) ? [] : [parseFilterQuery(query)]; - } catch { - return []; - } -}; - interface CreateResetGroupByFieldActionProps { callback?: () => void; order?: number; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx index 18b51365b0429..8a14499740444 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; import { mount } from 'enzyme'; import type { Filter } from '@kbn/es-query'; @@ -75,8 +75,6 @@ jest.mock('../../../../common/lib/kibana', () => { jest.mock('../../../../common/components/visualization_actions/visualization_embeddable'); -jest.mock('../../../../common/hooks/use_experimental_features'); - jest.mock('../../../../common/components/visualization_actions/use_visualization_response', () => { const original = jest.requireActual( '../../../../common/components/visualization_actions/use_visualization_response' @@ -371,30 +369,26 @@ describe('AlertsHistogramPanel', () => { }); describe('Query', () => { - it('it render with a illegal KQL', async () => { - await act(async () => { - jest.mock('@kbn/es-query', () => ({ - buildEsQuery: jest.fn().mockImplementation(() => { - throw new Error('Something went wrong'); - }), - })); - const props = { ...defaultProps, query: { query: 'host.name: "', language: 'kql' } }; - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...props} /> - </TestProviders> - ); + it('it render with a illegal KQL', () => { + jest.mock('@kbn/es-query', () => ({ + buildEsQuery: jest.fn().mockImplementation(() => { + throw new Error('Something went wrong'); + }), + })); + const props = { ...defaultProps, query: { query: 'host.name: "', language: 'kql' } }; + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...props} /> + </TestProviders> + ); - await waitFor(() => { - expect(wrapper.find('[data-test-subj="alerts-histogram-panel"]').exists()).toBeTruthy(); - }); - wrapper.unmount(); - }); + expect(wrapper.find('[data-test-subj="alerts-histogram-panel"]').exists()).toBeTruthy(); + wrapper.unmount(); }); }); describe('Filters', () => { - it('filters props is valid, alerts query include filter', async () => { + it('filters props is valid, alerts query include filter', () => { const statusFilter: Filter = { meta: { alias: null, @@ -423,119 +417,103 @@ describe('AlertsHistogramPanel', () => { </TestProviders> ); - await waitFor(() => { - expect( - (VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].timerange - ).toEqual({ - from: '2020-07-07T08:20:18.966Z', - to: '2020-07-08T08:20:18.966Z', - }); - expect( - (VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraOptions.filters - ).toEqual(props.filters); + expect((VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].timerange).toEqual({ + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', }); + expect( + (VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraOptions.filters + ).toEqual(props.filters); wrapper.unmount(); }); }); describe('toggle button', () => { describe('When setIsExpanded is available', () => { - it('toggles', async () => { - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...defaultProps} /> - </TestProviders> - ); - wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); - expect(mockSetIsExpanded).toBeCalledWith(false); - expect(mockSetToggle).not.toBeCalled(); - }); + it('toggles', () => { + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...defaultProps} /> + </TestProviders> + ); + + wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); + expect(mockSetIsExpanded).toBeCalledWith(false); + expect(mockSetToggle).not.toBeCalled(); + wrapper.unmount(); }); it('when isExpanded is true, render histogram panel', async () => { - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...defaultProps} isExpanded={true} /> - </TestProviders> - ); - expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(true); - expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( - true - ); - }); + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...defaultProps} isExpanded={true} /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( + true + ); + wrapper.unmount(); }); it('when isExpanded is false, hide histogram panel', async () => { - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...defaultProps} isExpanded={false} /> - </TestProviders> - ); - expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(false); - expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( - false - ); - }); + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...defaultProps} isExpanded={false} /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( + false + ); + wrapper.unmount(); }); }); describe('When setIsExpanded is not available, use toggleQuery', () => { - beforeEach(() => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle }); - }); const props = { ...defaultProps, setIsExpanded: undefined }; it('toggles', async () => { - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...props} /> - </TestProviders> - ); - wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); - expect(mockSetToggle).toBeCalledWith(false); - }); + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...props} /> + </TestProviders> + ); + wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); + expect(mockSetToggle).toBeCalledWith(false); + wrapper.unmount(); }); - it('when toggleStatus is true, render', async () => { - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...props} /> - </TestProviders> - ); - expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(true); - expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( - true - ); - }); + it('when toggleStatus is true, render', () => { + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...props} /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( + true + ); + wrapper.unmount(); }); it('when toggleStatus is false, hide', async () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: mockSetToggle }); - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...props} /> - </TestProviders> - ); - expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(false); - expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( - false - ); - }); + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...props} /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual( + false + ); + wrapper.unmount(); }); }); }); describe('VisualizationEmbeddable', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - test('it renders the header with alerts count', () => { const wrapper = mount( <TestProviders> @@ -552,50 +530,49 @@ describe('AlertsHistogramPanel', () => { }, ], }); + wrapper.setProps({ filters: [] }); wrapper.update(); expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toContain('999'); + wrapper.unmount(); }); - it('renders LensEmbeddable', async () => { - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...defaultProps} /> - </TestProviders> - ); - expect( - wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists() - ).toBeTruthy(); - }); + it('renders LensEmbeddable', () => { + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...defaultProps} /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toBeTruthy(); + wrapper.unmount(); }); - it('renders LensEmbeddable with provided height', async () => { - await act(async () => { - mount( - <TestProviders> - <AlertsHistogramPanel {...defaultProps} /> - </TestProviders> - ); - expect((VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].height).toEqual( - 155 - ); - }); + it('renders LensEmbeddable with provided height', () => { + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...defaultProps} /> + </TestProviders> + ); + + expect((VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].height).toEqual( + 155 + ); + wrapper.unmount(); }); - it('should render correct subtitle with alert count', async () => { - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...defaultProps} /> - </TestProviders> - ); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toContain('999'); - }); + it('should render correct subtitle with alert count', () => { + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...defaultProps} /> + </TestProviders> + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toContain('999'); + wrapper.unmount(); }); - it('should render correct subtitle with empty string', async () => { + it('should render correct subtitle with empty string', () => { (useVisualizationResponse as jest.Mock).mockReturnValue({ responses: [ { @@ -605,15 +582,14 @@ describe('AlertsHistogramPanel', () => { ], loading: false, }); + const wrapper = mount( + <TestProviders> + <AlertsHistogramPanel {...defaultProps} /> + </TestProviders> + ); - await act(async () => { - const wrapper = mount( - <TestProviders> - <AlertsHistogramPanel {...defaultProps} /> - </TestProviders> - ); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toEqual(''); - }); + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toEqual(''); + wrapper.unmount(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts index e2ab1a3ae9f84..df6f4e1573554 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts @@ -4,84 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -export const stackedByBooleanField = { - took: 1, - timed_out: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { - total: { - value: 3, - relation: 'eq', - }, - hits: [], - }, - timeout: false, - aggregations: { - alertsByGrouping: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 1, - key_as_string: 'true', - doc_count: 2683, - alerts: { - buckets: [ - { key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 }, - { key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 0 }, - { key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 0 }, - ], - }, - }, - ], - }, - }, -}; - -export const result = [ - { x: 1652196888075, y: 0, g: 'true' }, - { x: 1652199588074, y: 0, g: 'true' }, - { x: 1652202288073, y: 0, g: 'true' }, -]; - -export const stackedByTextField = { - took: 1, - timeout: false, - _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, - hits: { - total: { - value: 3, - relation: 'eq', - }, - hits: [], - }, - aggregations: { - alertsByGrouping: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'MacBook-Pro.local', - doc_count: 2706, - alerts: { - buckets: [ - { key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 }, - { key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 0 }, - { key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 0 }, - ], - }, - }, - ], - }, - }, -}; - -export const textResult = [ - { x: 1652196888075, y: 0, g: 'MacBook-Pro.local' }, - { x: 1652199588074, y: 0, g: 'MacBook-Pro.local' }, - { x: 1652202288073, y: 0, g: 'MacBook-Pro.local' }, -]; - export const mockAlertSearchResponse = { took: 1, timed_out: false, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/types.ts deleted file mode 100644 index ba0205577aa3a..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export interface HistogramData { - x: number; - y: number; - g: string; -} - -export interface AlertsAggregation { - alertsByGrouping: { - buckets: AlertsGroupBucket[]; - }; -} - -export interface AlertsBucket { - key_as_string: string; - key: number; - doc_count: number; -} - -export interface AlertsGroupBucket { - key: string | number; - key_as_string?: string; - alerts: { - buckets: AlertsBucket[]; - }; - doc_count: number; -} - -export interface AlertsTotal { - value: number; - relation: string; -} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx index 2cc5cda85436b..791f8eb9dc1ce 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx @@ -19,7 +19,8 @@ import type { GroupBySelection } from '../alerts_progress_bar_panel/types'; import type { AddFilterProps } from '../common/types'; const StyledFlexGroup = styled(EuiFlexGroup)` - @media only screen and (min-width: ${({ theme }) => theme.eui.euiBreakpoints.l}); + @media only screen and (min-width: ${({ theme }) => theme.eui.euiBreakpoints.l}) { + } `; const StyledFlexItem = styled(EuiFlexItem)` diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx index 4949fa9a2855f..22dd8da0e1101 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data.test.tsx @@ -14,7 +14,6 @@ import * as aggregations from './aggregations'; import * as severityMock from '../severity_level_panel/mock_data'; import * as alertRuleMock from '../alerts_by_rule_panel/mock_rule_data'; import * as alertsGroupingMock from '../alerts_progress_bar_panel/mock_data'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; const from = '2022-04-05T12:00:00.000Z'; const to = '2022-04-08T12:00:00.000Z'; @@ -50,9 +49,6 @@ jest.mock('../../../../common/containers/use_global_time', () => { }; }); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; -jest.mock('../../../../common/hooks/use_experimental_features'); - describe('getAlertsQuery', () => { test('it returns the expected severity query', () => { expect( @@ -167,7 +163,7 @@ describe('get summary charts data', () => { }); }); - describe('get alerts by type data', () => { + describe('get alerts by rule data', () => { beforeEach(() => { jest.clearAllMocks(); mockDateNow.mockReturnValue(dateNow); @@ -175,7 +171,6 @@ describe('get summary charts data', () => { }); it('should return correct default values', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); const { result } = renderUseSummaryChartData({ aggregations: aggregations.alertRuleAggregations, }); @@ -195,7 +190,6 @@ describe('get summary charts data', () => { }); it('should return parsed alerts by type items', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); mockUseQueryAlerts.mockReturnValue({ ...defaultUseQueryAlertsReturn, data: alertRuleMock.mockAlertsData, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/alerts_local_storage/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/alerts_local_storage/index.test.tsx index 6fd125dd24092..303b85a40e6ee 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/alerts_local_storage/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/alerts_local_storage/index.test.tsx @@ -10,10 +10,6 @@ import React from 'react'; import { useAlertsLocalStorage } from '.'; import { TestProviders } from '../../../../../common/mock'; -import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; - -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; -jest.mock('../../../../../common/hooks/use_experimental_features'); describe('useAlertsLocalStorage', () => { const wrapper = ({ children }: { children: React.ReactNode }) => ( @@ -21,7 +17,6 @@ describe('useAlertsLocalStorage', () => { ); test('it returns the expected defaults', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); const { result } = renderHook(() => useAlertsLocalStorage(), { wrapper }); const defaults = Object.fromEntries( diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx index dbeea09315331..0539b7f7615d6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/chart_collapse/index.tsx @@ -50,7 +50,8 @@ const combinedAggregations = (groupBySelection: GroupBySelection) => { const StyledEuiFlexGroup = styled(EuiFlexGroup)` margin-top: ${({ theme }) => theme.eui.euiSizeXS}; - @media only screen and (min-width: ${({ theme }) => theme.eui.euiBreakpoints.l}); + @media only screen and (min-width: ${({ theme }) => theme.eui.euiBreakpoints.l}) { + } `; const SeverityWrapper = styled(EuiFlexItem)` @@ -105,7 +106,7 @@ export const ChartCollapse: React.FC<Props> = ({ }); }, [data]); const groupBy = useMemo(() => getGroupByLabel(groupBySelection), [groupBySelection]); - // className="eui-alignMiddle" + return ( <InspectButtonContainer> {!isLoading && ( diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.test.tsx index 2b75252f3d578..668f323d147cb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/chart_panels/index.test.tsx @@ -16,7 +16,6 @@ import { mockBrowserFields } from '../../../../common/containers/source/mock'; import { useSourcererDataView } from '../../../../sourcerer/containers'; import { TestProviders } from '../../../../common/mock'; import { ChartPanels } from '.'; -import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { LensEmbeddable } from '../../../../common/components/visualization_actions/lens_embeddable'; import { createResetGroupByFieldAction } from '../alerts_histogram_panel/helpers'; @@ -27,6 +26,11 @@ jest.mock('../../../../sourcerer/containers'); jest.mock('../../../../common/components/visualization_actions/lens_embeddable'); jest.mock('../../../../common/components/page/use_refetch_by_session', () => ({ useRefetchByRestartingSession: jest.fn().mockReturnValue({ + session: { + current: { + start: jest.fn(), + }, + }, searchSessionId: 'mockSearchSessionId', refetchByRestartingSession: jest.fn(), }), @@ -63,9 +67,6 @@ jest.mock('../../../../common/lib/kibana', () => { }; }); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; -jest.mock('../../../../common/hooks/use_experimental_features'); - const mockSetToggle = jest.fn(); const mockUseQueryToggle = useQueryToggle as jest.Mock; jest.mock('../../../../common/containers/query_toggle'); @@ -163,7 +164,6 @@ describe('ChartPanels', () => { }); test('when toggle is true, renders the chart selector tabs', async () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle }); render( <TestProviders> @@ -177,7 +177,6 @@ describe('ChartPanels', () => { }); test('when toggle is false, renders the chart collapse', async () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: mockSetToggle }); render( <TestProviders> diff --git a/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/shared_lists.test.tsx b/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/shared_lists.test.tsx index c1167f0575fad..8d9100e0da94f 100644 --- a/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/shared_lists.test.tsx +++ b/x-pack/plugins/security_solution/public/exceptions/pages/shared_lists/shared_lists.test.tsx @@ -30,8 +30,13 @@ jest.mock('react-router-dom', () => { }; }); jest.mock('@kbn/i18n-react', () => { + const { i18n } = jest.requireActual('@kbn/i18n'); + i18n.init({ locale: 'en' }); + const originalModule = jest.requireActual('@kbn/i18n-react'); - const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); + const FormattedRelative = jest.fn(); + FormattedRelative.mockImplementationOnce(() => '2 days ago'); + FormattedRelative.mockImplementation(() => '20 hours ago'); return { ...originalModule, @@ -43,10 +48,7 @@ jest.mock('../../../detections/containers/detection_engine/lists/use_lists_confi useListsConfig: jest.fn().mockReturnValue({ loading: false }), })); -// FLAKY: https://github.com/elastic/kibana/issues/177670 -// FLAKY: https://github.com/elastic/kibana/issues/177671 -// FLAKY: https://github.com/elastic/kibana/issues/177672 -describe.skip('SharedLists', () => { +describe('SharedLists', () => { const mockHistory = generateHistoryMock(); const exceptionList1 = getExceptionListSchemaMock(); const exceptionList2 = { ...getExceptionListSchemaMock(), list_id: 'not_endpoint_list', id: '2' }; diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/get_processes_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/get_processes_action.tsx index 54dce8a6e4add..5086e1ac1fada 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/get_processes_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/get_processes_action.tsx @@ -6,12 +6,10 @@ */ import React, { memo, useMemo } from 'react'; +import type { GetProcessesRequestBody } from '../../../../../common/api/endpoint'; import { RunningProcessesActionResults } from '../../running_processes_action_results'; import { useConsoleActionSubmitter } from '../hooks/use_console_action_submitter'; -import type { - GetProcessesActionOutputContent, - ProcessesRequestBody, -} from '../../../../../common/endpoint/types'; +import type { GetProcessesActionOutputContent } from '../../../../../common/endpoint/types'; import { useSendGetEndpointProcessesRequest } from '../../../hooks/response_actions/use_send_get_endpoint_processes_request'; import type { ActionRequestComponentProps } from '../types'; @@ -32,7 +30,7 @@ export const GetProcessesActionResult = memo<ActionRequestComponentProps>( }, [endpointId, comment, agentType]); const { result, actionDetails: completedActionDetails } = useConsoleActionSubmitter< - ProcessesRequestBody, + GetProcessesRequestBody, GetProcessesActionOutputContent >({ ResultComponent, diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx index abfc48b78c791..e96f1f0028b7d 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/kill_process_action.tsx @@ -6,11 +6,11 @@ */ import { memo, useMemo } from 'react'; +import type { KillProcessRequestBody } from '../../../../../common/api/endpoint'; import { parsedKillOrSuspendParameter } from '../lib/utils'; import { useSendKillProcessRequest } from '../../../hooks/response_actions/use_send_kill_process_endpoint_request'; import type { ActionRequestComponentProps } from '../types'; import { useConsoleActionSubmitter } from '../hooks/use_console_action_submitter'; -import type { KillProcessRequestBody } from '../../../../../common/endpoint/types'; export const KillProcessActionResult = memo< ActionRequestComponentProps<{ pid?: string[]; entityId?: string[]; processName?: string[] }> diff --git a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx index 344ebe5da626e..b2a73b426aa27 100644 --- a/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx +++ b/x-pack/plugins/security_solution/public/management/components/endpoint_responder/command_render_components/suspend_process_action.tsx @@ -6,10 +6,10 @@ */ import { memo, useMemo } from 'react'; +import type { SuspendProcessRequestBody } from '../../../../../common/api/endpoint'; import { parsedKillOrSuspendParameter } from '../lib/utils'; import type { SuspendProcessActionOutputContent, - SuspendProcessRequestBody, ResponseActionParametersWithEntityId, ResponseActionParametersWithPid, } from '../../../../../common/endpoint/types'; diff --git a/x-pack/plugins/security_solution/public/management/cypress/support/saml_authentication.ts b/x-pack/plugins/security_solution/public/management/cypress/support/saml_authentication.ts index a7085494ab6e8..526c430920ea6 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/support/saml_authentication.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/support/saml_authentication.ts @@ -10,6 +10,7 @@ import { ToolingLog } from '@kbn/tooling-log'; import type { HostOptions } from '@kbn/test'; import { SamlSessionManager } from '@kbn/test'; import type { SecurityRoleName } from '../../../../common/test'; +import { resolveCloudUsersFilePath } from '../../../../scripts/endpoint/common/roles_users/serverless'; export const samlAuthentication = async ( on: Cypress.PluginEvents, @@ -34,15 +35,15 @@ export const samlAuthentication = async ( role: string | SecurityRoleName ): Promise<{ cookie: string; username: string; password: string }> => { // If config.env.PROXY_ORG is set, it means that proxy service is used to create projects. Define the proxy org filename to override the roles. - const rolesFilename = config.env.PROXY_ORG ? `${config.env.PROXY_ORG}.json` : undefined; - const sessionManager = new SamlSessionManager( - { - hostOptions, - log, - isCloud: config.env.CLOUD_SERVERLESS, - }, - rolesFilename - ); + const rolesFilename = config.env.PROXY_ORG + ? `${config.env.PROXY_ORG}.json` + : 'role_users.json'; + const sessionManager = new SamlSessionManager({ + hostOptions, + log, + isCloud: config.env.CLOUD_SERVERLESS, + cloudUsersFilePath: resolveCloudUsersFilePath(rolesFilename), + }); return sessionManager.getInteractiveUserSessionCookieWithRoleScope(role).then((cookie) => { return { cookie, diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_get_endpoint_processes_request.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_get_endpoint_processes_request.ts index cf946db04ca37..849380714cd94 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_get_endpoint_processes_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_get_endpoint_processes_request.ts @@ -8,8 +8,8 @@ import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; +import type { GetProcessesRequestBody } from '../../../../common/api/endpoint'; import type { - ProcessesRequestBody, ResponseActionApiResponse, GetProcessesActionOutputContent, } from '../../../../common/endpoint/types/actions'; @@ -24,18 +24,18 @@ export const useSendGetEndpointProcessesRequest = ( customOptions?: UseMutationOptions< ResponseActionApiResponse<GetProcessesActionOutputContent>, IHttpFetchError, - ProcessesRequestBody + GetProcessesRequestBody > ): UseMutationResult< ResponseActionApiResponse<GetProcessesActionOutputContent>, IHttpFetchError, - ProcessesRequestBody + GetProcessesRequestBody > => { return useMutation< ResponseActionApiResponse<GetProcessesActionOutputContent>, IHttpFetchError, - ProcessesRequestBody - >((getRunningProcessesData: ProcessesRequestBody) => { + GetProcessesRequestBody + >((getRunningProcessesData: GetProcessesRequestBody) => { return KibanaServices.get().http.post< ResponseActionApiResponse<GetProcessesActionOutputContent> >(GET_PROCESSES_ROUTE, { diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_isolate_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_isolate_endpoint_request.ts index b4cb131f6ae9c..a9efa834e97e5 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_isolate_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_isolate_endpoint_request.ts @@ -8,11 +8,9 @@ import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; +import type { IsolationRouteRequestBody } from '../../../../common/api/endpoint'; import { isolateHost } from '../../../common/lib/endpoint/endpoint_isolation'; -import type { - HostIsolationRequestBody, - ResponseActionApiResponse, -} from '../../../../common/endpoint/types'; +import type { ResponseActionApiResponse } from '../../../../common/endpoint/types'; /** * Create host isolation requests @@ -22,11 +20,11 @@ export const useSendIsolateEndpointRequest = ( customOptions?: UseMutationOptions< ResponseActionApiResponse, IHttpFetchError, - HostIsolationRequestBody + IsolationRouteRequestBody > -): UseMutationResult<ResponseActionApiResponse, IHttpFetchError, HostIsolationRequestBody> => { - return useMutation<ResponseActionApiResponse, IHttpFetchError, HostIsolationRequestBody>( - (isolateData: HostIsolationRequestBody) => { +): UseMutationResult<ResponseActionApiResponse, IHttpFetchError, IsolationRouteRequestBody> => { + return useMutation<ResponseActionApiResponse, IHttpFetchError, IsolationRouteRequestBody>( + (isolateData: IsolationRouteRequestBody) => { return isolateHost(isolateData); }, customOptions diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts index b17d34ab4e463..1dd2b1ae2a8c7 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_kill_process_endpoint_request.ts @@ -8,10 +8,8 @@ import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { - ResponseActionApiResponse, - KillProcessRequestBody, -} from '../../../../common/endpoint/types'; +import type { KillProcessRequestBody } from '../../../../common/api/endpoint'; +import type { ResponseActionApiResponse } from '../../../../common/endpoint/types'; import { killProcess } from '../../../common/lib/process_actions'; /** diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_release_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_release_endpoint_request.ts index b9e140dc3ba9d..ed0eaf68f90d4 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_release_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_release_endpoint_request.ts @@ -8,10 +8,8 @@ import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { - HostIsolationRequestBody, - ResponseActionApiResponse, -} from '../../../../common/endpoint/types'; +import type { UnisolationRouteRequestBody } from '../../../../common/api/endpoint'; +import type { ResponseActionApiResponse } from '../../../../common/endpoint/types'; import { unIsolateHost } from '../../../common/lib/endpoint/endpoint_isolation'; /** @@ -22,11 +20,11 @@ export const useSendReleaseEndpointRequest = ( customOptions?: UseMutationOptions< ResponseActionApiResponse, IHttpFetchError, - HostIsolationRequestBody + UnisolationRouteRequestBody > -): UseMutationResult<ResponseActionApiResponse, IHttpFetchError, HostIsolationRequestBody> => { - return useMutation<ResponseActionApiResponse, IHttpFetchError, HostIsolationRequestBody>( - (releaseData: HostIsolationRequestBody) => { +): UseMutationResult<ResponseActionApiResponse, IHttpFetchError, UnisolationRouteRequestBody> => { + return useMutation<ResponseActionApiResponse, IHttpFetchError, UnisolationRouteRequestBody>( + (releaseData: UnisolationRouteRequestBody) => { return unIsolateHost(releaseData); }, customOptions diff --git a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts index 787344ffd54dc..f2e467e36073e 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts +++ b/x-pack/plugins/security_solution/public/management/hooks/response_actions/use_send_suspend_process_endpoint_request.ts @@ -8,10 +8,8 @@ import { useMutation } from '@tanstack/react-query'; import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import type { - ResponseActionApiResponse, - SuspendProcessRequestBody, -} from '../../../../common/endpoint/types'; +import type { SuspendProcessRequestBody } from '../../../../common/api/endpoint'; +import type { ResponseActionApiResponse } from '../../../../common/endpoint/types'; import { suspendProcess } from '../../../common/lib/process_actions'; /** diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts index 01b9b3455dea9..43db8f92929e8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts @@ -7,9 +7,9 @@ import type { Action } from 'redux'; import type { DataViewBase } from '@kbn/es-query'; +import type { IsolationRouteRequestBody } from '../../../../../common/api/endpoint'; import type { GetHostPolicyResponse, - HostIsolationRequestBody, ISOLATION_ACTIONS, MetadataListResponse, } from '../../../../../common/endpoint/types'; @@ -133,7 +133,7 @@ export interface ServerFailedToReturnEndpointsTotal { export type EndpointIsolationRequest = Action<'endpointIsolationRequest'> & { payload: { type: ISOLATION_ACTIONS; - data: HostIsolationRequestBody; + data: IsolationRouteRequestBody; }; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index fce262052220e..ec27500a45e12 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -14,6 +14,10 @@ import type { IndexFieldsStrategyRequest, IndexFieldsStrategyResponse, } from '@kbn/timelines-plugin/common'; +import type { + IsolationRouteRequestBody, + UnisolationRouteRequestBody, +} from '../../../../../common/api/endpoint'; import { ENDPOINT_FIELDS_SEARCH_STRATEGY, HOST_METADATA_LIST_ROUTE, @@ -22,7 +26,6 @@ import { metadataCurrentIndexPattern, } from '../../../../../common/endpoint/constants'; import type { - HostIsolationRequestBody, HostResultList, Immutable, ImmutableObject, @@ -246,9 +249,9 @@ const handleIsolateEndpointHost = async ( let response: ResponseActionApiResponse; if (action.payload.type === 'unisolate') { - response = await unIsolateHost(action.payload.data as HostIsolationRequestBody); + response = await unIsolateHost(action.payload.data as UnisolationRouteRequestBody); } else { - response = await isolateHost(action.payload.data as HostIsolationRequestBody); + response = await isolateHost(action.payload.data as IsolationRouteRequestBody); } dispatch({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx index a5a22fd70f563..0dc8c4367e4fe 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/fields_browser/index.test.tsx @@ -236,7 +236,7 @@ describe('useFieldBrowserOptions', () => { it('should dispatch the proper actions when a field is removed', async () => { let onDelete: ((fields: string[]) => void) | undefined; useKibanaMock().services.data.dataViews.get = () => Promise.resolve({} as DataView); - useKibanaMock().services.dataViewFieldEditor.openDeleteModal = (options) => { + useKibanaMock().services.dataViewFieldEditor.openDeleteModal = async (options) => { onDelete = options.onDelete; return () => {}; }; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/index.ts index 23a44df2d0808..f743f4e3db10c 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/index.ts @@ -7,6 +7,7 @@ import { resolve, join } from 'path'; import { readFileSync } from 'fs'; +import { REPO_ROOT } from '@kbn/repo-info'; const ES_RESOURCES_DIR = resolve(__dirname, 'es_serverless_resources'); @@ -16,6 +17,8 @@ export const ES_RESOURCES = Object.freeze({ users_roles: join(ES_RESOURCES_DIR, 'users_roles'), }); +export const resolveCloudUsersFilePath = (filename: string) => resolve(REPO_ROOT, '.ftr', filename); + export const ES_LOADED_USERS = readFileSync(ES_RESOURCES.users) .toString() .split(/\n/) diff --git a/x-pack/plugins/security_solution/scripts/telemetry/saved_objects/security_solution_ebt_kibana_server.ndjson b/x-pack/plugins/security_solution/scripts/telemetry/saved_objects/security_solution_ebt_kibana_server.ndjson index ee1293900f4ec..9b69cdc5a8493 100644 --- a/x-pack/plugins/security_solution/scripts/telemetry/saved_objects/security_solution_ebt_kibana_server.ndjson +++ b/x-pack/plugins/security_solution/scripts/telemetry/saved_objects/security_solution_ebt_kibana_server.ndjson @@ -1,2 +1,2 @@ -{"attributes":{"allowHidden":false,"fieldAttrs":"{\"properties.model\":{},\"properties.resourceAccessed\":{},\"properties.resultCount\":{},\"properties.responseTime\":{},\"properties.errorMessage\":{},\"properties.isEnabledKnowledgeBase\":{},\"properties.isEnabledRAGAlerts\":{},\"properties.assistantStreamingEnabled\":{},\"properties.actionTypeId\":{},\"properties.message\":{},\"properties.productTier\":{},\"properties.failedToDeleteCount\":{},\"properties.totalInstalledCount\":{},\"properties.scoresWritten\":{},\"properties.taskDurationInSeconds\":{},\"properties.interval\":{},\"properties.alertSampleSizePerShard\":{},\"properties.status\":{},\"properties.processing.startTime\":{},\"properties.processing.endTime\":{},\"properties.processing.tookMs\":{},\"properties.result.successful\":{},\"properties.result.failed\":{},\"properties.result.total\":{}}","fieldFormatMap":"{}","fields":"[]","name":"security-solution-ebt-kibana-server","runtimeFieldMap":"{\"properties.message\":{\"type\":\"keyword\"},\"properties.productTier\":{\"type\":\"keyword\"},\"properties.failedToDeleteCount\":{\"type\":\"long\"},\"properties.totalInstalledCount\":{\"type\":\"long\"},\"properties.model\":{\"type\":\"keyword\"},\"properties.resourceAccessed\":{\"type\":\"keyword\"},\"properties.resultCount\":{\"type\":\"long\"},\"properties.responseTime\":{\"type\":\"long\"},\"properties.errorMessage\":{\"type\":\"keyword\"},\"properties.isEnabledKnowledgeBase\":{\"type\":\"boolean\"},\"properties.isEnabledRAGAlerts\":{\"type\":\"boolean\"},\"properties.assistantStreamingEnabled\":{\"type\":\"boolean\"},\"properties.actionTypeId\":{\"type\":\"keyword\"},\"properties.scoresWritten\":{\"type\":\"long\"},\"properties.taskDurationInSeconds\":{\"type\":\"long\"},\"properties.interval\":{\"type\":\"keyword\"},\"properties.alertSampleSizePerShard\":{\"type\":\"long\"},\"properties.status\":{\"type\":\"keyword\"},\"properties.processing.startTime\":{\"type\":\"date\"},\"properties.processing.endTime\":{\"type\":\"date\"},\"properties.processing.tookMs\":{\"type\":\"long\"},\"properties.result.successful\":{\"type\":\"long\"},\"properties.result.failed\":{\"type\":\"long\"},\"properties.result.total\":{\"type\":\"long\"}}","sourceFilters":"[]","timeFieldName":"timestamp","title":"ebt-kibana-server"},"coreMigrationVersion":"8.8.0","created_at":"2024-05-30T16:12:44.874Z","id":"security-solution-ebt-kibana-server","managed":false,"references":[],"type":"index-pattern","typeMigrationVersion":"8.0.0","updated_at":"2024-05-30T16:52:11.038Z","version":"WzMwNTYxLDVd"} -{"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":1,"missingRefCount":0,"missingReferences":[]} +{"attributes":{"allowHidden":false,"fieldAttrs":"{\"properties.model\":{},\"properties.resourceAccessed\":{},\"properties.resultCount\":{},\"properties.responseTime\":{},\"properties.errorMessage\":{},\"properties.isEnabledKnowledgeBase\":{},\"properties.isEnabledRAGAlerts\":{},\"properties.assistantStreamingEnabled\":{},\"properties.actionTypeId\":{},\"properties.message\":{},\"properties.productTier\":{},\"properties.failedToDeleteCount\":{},\"properties.totalInstalledCount\":{},\"properties.scoresWritten\":{},\"properties.taskDurationInSeconds\":{},\"properties.interval\":{},\"properties.alertSampleSizePerShard\":{},\"properties.status\":{},\"properties.processing.startTime\":{},\"properties.processing.endTime\":{},\"properties.processing.tookMs\":{},\"properties.result.successful\":{},\"properties.result.failed\":{},\"properties.result.total\":{},\"properties.alertsContextCount\":{},\"properties.alertsCount\":{},\"properties.configuredAlertsCount\":{},\"properties.discoveriesGenerated\":{},\"properties.durationMs\":{},\"properties.provider\":{},\"properties.total_tokens\":{},\"properties.prompt_tokens\":{},\"properties.completion_tokens\":{},\"properties.suppressionRuleType\":{},\"properties.suppressionMissingFields\":{},\"properties.suppressionAlertsCreated\":{},\"properties.suppressionAlertsSuppressed\":{},\"properties.suppressionRuleName\":{},\"properties.suppressionDuration\":{},\"properties.suppressionFieldsNumber\":{},\"properties.suppressionGroupByFieldsNumber\":{},\"properties.suppressionGroupByFields\":{},\"properties.suppressionRuleId\":{}}","fieldFormatMap":"{}","fields":"[]","name":"security-solution-ebt-kibana-server","runtimeFieldMap":"{\"properties.message\":{\"type\":\"keyword\"},\"properties.productTier\":{\"type\":\"keyword\"},\"properties.failedToDeleteCount\":{\"type\":\"long\"},\"properties.totalInstalledCount\":{\"type\":\"long\"},\"properties.model\":{\"type\":\"keyword\"},\"properties.resourceAccessed\":{\"type\":\"keyword\"},\"properties.resultCount\":{\"type\":\"long\"},\"properties.responseTime\":{\"type\":\"long\"},\"properties.errorMessage\":{\"type\":\"keyword\"},\"properties.isEnabledKnowledgeBase\":{\"type\":\"boolean\"},\"properties.isEnabledRAGAlerts\":{\"type\":\"boolean\"},\"properties.assistantStreamingEnabled\":{\"type\":\"boolean\"},\"properties.actionTypeId\":{\"type\":\"keyword\"},\"properties.alertsContextCount\":{\"type\":\"long\"},\"properties.alertsCount\":{\"type\":\"long\"},\"properties.configuredAlertsCount\":{\"type\":\"long\"},\"properties.discoveriesGenerated\":{\"type\":\"long\"},\"properties.durationMs\":{\"type\":\"long\"},\"properties.provider\":{\"type\":\"keyword\"},\"properties.scoresWritten\":{\"type\":\"long\"},\"properties.taskDurationInSeconds\":{\"type\":\"long\"},\"properties.interval\":{\"type\":\"keyword\"},\"properties.alertSampleSizePerShard\":{\"type\":\"long\"},\"properties.status\":{\"type\":\"keyword\"},\"properties.processing.startTime\":{\"type\":\"date\"},\"properties.processing.endTime\":{\"type\":\"date\"},\"properties.processing.tookMs\":{\"type\":\"long\"},\"properties.result.successful\":{\"type\":\"long\"},\"properties.result.failed\":{\"type\":\"long\"},\"properties.result.total\":{\"type\":\"long\"},\"properties.total_tokens\":{\"type\":\"long\"},\"properties.prompt_tokens\":{\"type\":\"long\"},\"properties.completion_tokens\":{\"type\":\"keyword\"},\"properties.suppressionMissingFields\":{\"type\":\"boolean\"},\"properties.suppressionAlertsCreated\":{\"type\":\"long\"},\"properties.suppressionAlertsSuppressed\":{\"type\":\"long\"},\"properties.suppressionRuleName\":{\"type\":\"keyword\"},\"properties.suppressionDuration\":{\"type\":\"long\"},\"properties.suppressionRuleType\":{\"type\":\"keyword\"},\"properties.suppressionGroupByFieldsNumber\":{\"type\":\"long\"},\"properties.suppressionGroupByFields\":{\"type\":\"keyword\"},\"properties.suppressionRuleId\":{\"type\":\"keyword\"}}","sourceFilters":"[]","timeFieldName":"timestamp","title":"ebt-kibana-server"},"coreMigrationVersion":"8.8.0","created_at":"2024-05-30T16:12:44.874Z","id":"security-solution-ebt-kibana-server","managed":false,"references":[],"type":"index-pattern","typeMigrationVersion":"8.0.0","updated_at":"2024-07-30T11:12:43.928Z","version":"WzM4ODczLDVd"} +{"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":1,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/x-pack/plugins/security_solution/server/client/client.test.ts b/x-pack/plugins/security_solution/server/client/client.test.ts index 4c0c4361b3a72..59be6609b3374 100644 --- a/x-pack/plugins/security_solution/server/client/client.test.ts +++ b/x-pack/plugins/security_solution/server/client/client.test.ts @@ -17,7 +17,7 @@ describe('SiemClient', () => { [SIGNALS_INDEX_KEY]: 'mockSignalsIndex', }; const spaceId = 'fooSpace'; - const client = new AppClient(spaceId, mockConfig, '8.7', 'main'); + const client = new AppClient(spaceId, mockConfig, '8.7', 'main', 'traditional'); expect(client.getSignalsIndex()).toEqual('mockSignalsIndex-fooSpace'); }); diff --git a/x-pack/plugins/security_solution/server/client/client.ts b/x-pack/plugins/security_solution/server/client/client.ts index c7e63769ac1e0..b2418e0263699 100644 --- a/x-pack/plugins/security_solution/server/client/client.ts +++ b/x-pack/plugins/security_solution/server/client/client.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { BuildFlavor } from '@kbn/config'; import type { ConfigType } from '../config'; import { DEFAULT_ALERTS_INDEX, @@ -20,8 +21,15 @@ export class AppClient { private readonly sourcererDataViewId: string; private readonly kibanaVersion: string; private readonly kibanaBranch: string; + private readonly buildFlavor: BuildFlavor; - constructor(spaceId: string, config: ConfigType, kibanaVersion: string, kibanaBranch: string) { + constructor( + spaceId: string, + config: ConfigType, + kibanaVersion: string, + kibanaBranch: string, + buildFlavor: BuildFlavor + ) { const configuredSignalsIndex = config.signalsIndex; this.alertsIndex = `${DEFAULT_ALERTS_INDEX}-${spaceId}`; @@ -31,6 +39,7 @@ export class AppClient { this.spaceId = spaceId; this.kibanaVersion = kibanaVersion; this.kibanaBranch = kibanaBranch; + this.buildFlavor = buildFlavor; } public getAlertsIndex = (): string => this.alertsIndex; @@ -40,4 +49,5 @@ export class AppClient { public getSpaceId = (): string => this.spaceId; public getKibanaVersion = (): string => this.kibanaVersion; public getKibanaBranch = (): string => this.kibanaBranch; + public getBuildFlavor = (): BuildFlavor => this.buildFlavor; } diff --git a/x-pack/plugins/security_solution/server/client/factory.test.ts b/x-pack/plugins/security_solution/server/client/factory.test.ts index 681daf88fc0d9..81f9f73d759a7 100644 --- a/x-pack/plugins/security_solution/server/client/factory.test.ts +++ b/x-pack/plugins/security_solution/server/client/factory.test.ts @@ -23,6 +23,7 @@ describe('AppClientFactory', () => { config: createMockConfig(), kibanaVersion: '8.7', kibanaBranch: 'main', + buildFlavor: 'traditional', }); factory.create(mockRequest); @@ -30,6 +31,7 @@ describe('AppClientFactory', () => { 'mockSpace', expect.anything(), expect.anything(), + expect.anything(), expect.anything() ); }); @@ -42,6 +44,7 @@ describe('AppClientFactory', () => { config: createMockConfig(), kibanaVersion: '8.7', kibanaBranch: 'main', + buildFlavor: 'traditional', }); factory.create(mockRequest); @@ -49,6 +52,7 @@ describe('AppClientFactory', () => { 'default', expect.anything(), expect.anything(), + expect.anything(), expect.anything() ); }); diff --git a/x-pack/plugins/security_solution/server/client/factory.ts b/x-pack/plugins/security_solution/server/client/factory.ts index 3dc8925c0c6e3..d36b076e55531 100644 --- a/x-pack/plugins/security_solution/server/client/factory.ts +++ b/x-pack/plugins/security_solution/server/client/factory.ts @@ -6,6 +6,7 @@ */ import type { KibanaRequest } from '@kbn/core/server'; +import type { BuildFlavor } from '@kbn/config'; import { AppClient } from './client'; import type { ConfigType } from '../config'; import { invariant } from '../../common/utils/invariant'; @@ -15,6 +16,7 @@ interface SetupDependencies { config: ConfigType; kibanaVersion: string; kibanaBranch: string; + buildFlavor: BuildFlavor; } export class AppClientFactory { @@ -22,12 +24,20 @@ export class AppClientFactory { private config?: SetupDependencies['config']; private kibanaVersion?: string; private kibanaBranch?: string; + private buildFlavor?: BuildFlavor; - public setup({ getSpaceId, config, kibanaBranch, kibanaVersion }: SetupDependencies) { + public setup({ + getSpaceId, + config, + kibanaBranch, + kibanaVersion, + buildFlavor, + }: SetupDependencies) { this.getSpaceId = getSpaceId; this.config = config; this.kibanaVersion = kibanaVersion; this.kibanaBranch = kibanaBranch; + this.buildFlavor = buildFlavor; } public create(request: KibanaRequest): AppClient { @@ -43,8 +53,18 @@ export class AppClientFactory { this.kibanaBranch != null, 'Cannot create AppClient as kibanaBranch is not present. Did you forget to call setup()?' ); + invariant( + this.buildFlavor != null, + 'Cannot create AppClient as buildFlavor is not present. Did you forget to call setup()?' + ); const spaceId = this.getSpaceId?.(request) ?? 'default'; - return new AppClient(spaceId, this.config, this.kibanaVersion, this.kibanaBranch); + return new AppClient( + spaceId, + this.config, + this.kibanaVersion, + this.kibanaBranch, + this.buildFlavor + ); } } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts index 47099ae060efa..2fdfa5143cfe2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.test.ts @@ -42,7 +42,6 @@ import type { HostMetadata, LogsEndpointAction, ResponseActionApiResponse, - ResponseActionRequestBody, } from '../../../../common/endpoint/types'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; import type { EndpointAuthz } from '../../../../common/endpoint/types/authz'; @@ -63,7 +62,10 @@ import * as ActionDetailsService from '../../services/actions/action_details_by_ import { CaseStatuses } from '@kbn/cases-components'; import { getEndpointAuthzInitialStateMock } from '../../../../common/endpoint/service/authz/mocks'; import { getResponseActionsClient as _getResponseActionsClient } from '../../services'; -import type { UploadActionApiRequestBody } from '../../../../common/api/endpoint'; +import type { + ResponseActionsRequestBody, + UploadActionApiRequestBody, +} from '../../../../common/api/endpoint'; import type { FleetToHostFileClientInterface } from '@kbn/fleet-plugin/server'; import type { HapiReadableStream, SecuritySolutionRequestHandlerContext } from '../../../types'; import { createHapiReadableStreamMock } from '../../services/actions/mocks'; @@ -92,7 +94,7 @@ jest.mock('../../services', () => { const getResponseActionsClientMock = _getResponseActionsClient; interface CallRouteInterface { - body?: ResponseActionRequestBody; + body?: ResponseActionsRequestBody; indexErrorResponse?: any; searchResponse?: HostMetadata; mockUser?: any; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts index 723daf576c38e..9f40852fec380 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/response_actions.ts @@ -13,6 +13,10 @@ import { stringify } from '../../utils/stringify'; import { getResponseActionsClient, NormalizedExternalConnectorClient } from '../../services'; import type { ResponseActionsClient } from '../../services/actions/clients/lib/types'; import { CustomHttpRequestError } from '../../../utils/custom_http_request_error'; +import type { + KillProcessRequestBody, + SuspendProcessRequestBody, +} from '../../../../common/api/endpoint'; import { EndpointActionGetFileSchema, type ExecuteActionRequestBody, @@ -50,8 +54,6 @@ import type { ResponseActionParametersWithProcessData, ResponseActionsExecuteParameters, ResponseActionScanParameters, - KillProcessRequestBody, - SuspendProcessRequestBody, } from '../../../../common/endpoint/types'; import type { ResponseActionsApiCommandNames } from '../../../../common/endpoint/service/response_actions/constants'; import type { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/handlers.ts index e1677451ff577..79eb38211387e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/handlers.ts @@ -16,7 +16,7 @@ import { protectionUpdatesNoteSavedObjectType } from '../../lib/protection_updat import type { CreateUpdateProtectionUpdatesNoteSchema, GetProtectionUpdatesNoteSchema, -} from '../../../../common/api/endpoint/protection_updates_note/protection_updates_note_schema'; +} from '../../../../common/api/endpoint/protection_updates_note'; const getProtectionNote = async (SOClient: SavedObjectsClientContract, packagePolicyId: string) => { return SOClient.find<{ note: string }>({ diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts index 4d398bbe14e6e..7b28ccfcf9fe7 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/protection_updates_note/index.ts @@ -10,7 +10,7 @@ import { getProtectionUpdatesNoteHandler, postProtectionUpdatesNoteHandler } fro import { GetProtectionUpdatesNoteSchema, CreateUpdateProtectionUpdatesNoteSchema, -} from '../../../../common/api/endpoint/protection_updates_note/protection_updates_note_schema'; +} from '../../../../common/api/endpoint/protection_updates_note'; import { withEndpointAuthz } from '../with_endpoint_authz'; import { PROTECTION_UPDATES_NOTE_ROUTE } from '../../../../common/endpoint/constants'; import type { EndpointAppContext } from '../../types'; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts index 958c51014c6a0..b39c85726651f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/crowdstrike/crowdstrike_actions_client.ts @@ -27,7 +27,10 @@ import type { EndpointActionResponseDataOutput, LogsEndpointAction, } from '../../../../../../common/endpoint/types'; -import type { IsolationRouteRequestBody } from '../../../../../../common/api/endpoint'; +import type { + IsolationRouteRequestBody, + UnisolationRouteRequestBody, +} from '../../../../../../common/api/endpoint'; import type { ResponseActionsClientOptions, ResponseActionsClientWriteActionRequestToEndpointIndexOptions, @@ -238,7 +241,7 @@ export class CrowdstrikeActionsClient extends ResponseActionsClientImpl { } async release( - actionRequest: IsolationRouteRequestBody, + actionRequest: UnisolationRouteRequestBody, options: CommonResponseActionMethodOptions = {} ): Promise<ActionDetails> { const reqIndexOptions: ResponseActionsClientWriteActionRequestToEndpointIndexOptions = { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts index eaaa5fa259927..a406397c2be19 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.test.ts @@ -11,20 +11,21 @@ import { EndpointActionsClient } from '../../..'; import { endpointActionClientMock } from './mocks'; import { responseActionsClientMock } from '../mocks'; import { ENDPOINT_ACTIONS_INDEX } from '../../../../../../common/endpoint/constants'; -import type { ResponseActionRequestBody } from '../../../../../../common/endpoint/types'; + import { DEFAULT_EXECUTE_ACTION_TIMEOUT } from '../../../../../../common/endpoint/service/response_actions/constants'; import { applyEsClientSearchMock } from '../../../../mocks/utils.mock'; import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { BaseDataGenerator } from '../../../../../../common/endpoint/data_generators/base_data_generator'; import { Readable } from 'stream'; import { EndpointActionGenerator } from '../../../../../../common/endpoint/data_generators/endpoint_action_generator'; +import type { ResponseActionsRequestBody } from '../../../../../../common/api/endpoint'; describe('EndpointActionsClient', () => { let classConstructorOptions: ResponseActionsClientOptions; let endpointActionsClient: ResponseActionsClient; const getCommonResponseActionOptions = (): Pick< - ResponseActionRequestBody, + ResponseActionsRequestBody, 'endpoint_ids' | 'case_ids' > => { return { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts index 59328beb46c12..df2b3c323ee08 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/endpoint/endpoint_actions_client.ts @@ -24,6 +24,9 @@ import type { UploadActionApiRequestBody, ResponseActionsRequestBody, ScanActionRequestBody, + SuspendProcessRequestBody, + KillProcessRequestBody, + UnisolationRouteRequestBody, } from '../../../../../../common/api/endpoint'; import { ResponseActionsClientImpl } from '../lib/base_response_actions_client'; import type { @@ -44,8 +47,6 @@ import type { UploadedFileInfo, ResponseActionScanParameters, ResponseActionScanOutputContent, - KillProcessRequestBody, - SuspendProcessRequestBody, } from '../../../../../../common/endpoint/types'; import type { CommonResponseActionMethodOptions, @@ -236,10 +237,10 @@ export class EndpointActionsClient extends ResponseActionsClientImpl { } async release( - actionRequest: IsolationRouteRequestBody, + actionRequest: UnisolationRouteRequestBody, options: CommonResponseActionMethodOptions = {} ): Promise<ActionDetails> { - return this.handleResponseAction<IsolationRouteRequestBody, ActionDetails>( + return this.handleResponseAction<UnisolationRouteRequestBody, ActionDetails>( 'unisolate', actionRequest, options diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts index 67aec8d861ce4..07ab63b77a312 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/base_response_actions_client.ts @@ -66,16 +66,17 @@ import type { SuspendProcessActionOutputContent, UploadedFileInfo, WithAllKeys, - KillProcessRequestBody, - SuspendProcessRequestBody, } from '../../../../../../common/endpoint/types'; import type { ExecuteActionRequestBody, GetProcessesRequestBody, IsolationRouteRequestBody, + KillProcessRequestBody, ResponseActionGetFileRequestBody, ResponseActionsRequestBody, ScanActionRequestBody, + SuspendProcessRequestBody, + UnisolationRouteRequestBody, UploadActionApiRequestBody, } from '../../../../../../common/api/endpoint'; import { stringify } from '../../../../utils/stringify'; @@ -716,7 +717,7 @@ export abstract class ResponseActionsClientImpl implements ResponseActionsClient } public async release( - actionRequest: IsolationRouteRequestBody, + actionRequest: UnisolationRouteRequestBody, options?: CommonResponseActionMethodOptions ): Promise<ActionDetails> { throw new ResponseActionsNotSupportedError('unisolate'); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts index 4a7b7efd4d4a5..e3407b5ba959a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/lib/types.ts @@ -23,17 +23,18 @@ import type { UploadedFileInfo, ResponseActionScanOutputContent, ResponseActionScanParameters, - KillProcessRequestBody, - SuspendProcessRequestBody, } from '../../../../../../common/endpoint/types'; import type { IsolationRouteRequestBody, + UnisolationRouteRequestBody, GetProcessesRequestBody, ResponseActionGetFileRequestBody, ExecuteActionRequestBody, UploadActionApiRequestBody, BaseActionRequestBody, ScanActionRequestBody, + KillProcessRequestBody, + SuspendProcessRequestBody, } from '../../../../../../common/api/endpoint'; type OmitUnsupportedAttributes<T extends BaseActionRequestBody> = Omit< @@ -84,7 +85,7 @@ export interface ResponseActionsClient { ) => Promise<ActionDetails>; release: ( - actionRequest: OmitUnsupportedAttributes<IsolationRouteRequestBody>, + actionRequest: OmitUnsupportedAttributes<UnisolationRouteRequestBody>, options?: CommonResponseActionMethodOptions ) => Promise<ActionDetails>; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts index 906125df4c2b8..2933f25cfd0ad 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions/clients/sentinelone/sentinel_one_actions_client.ts @@ -57,7 +57,6 @@ import type { EndpointActionResponseDataOutput, GetProcessesActionOutputContent, KillProcessActionOutputContent, - KillProcessRequestBody, LogsEndpointAction, LogsEndpointActionResponse, ResponseActionGetFileOutputContent, @@ -81,6 +80,8 @@ import type { GetProcessesRequestBody, IsolationRouteRequestBody, ResponseActionGetFileRequestBody, + KillProcessRequestBody, + UnisolationRouteRequestBody, } from '../../../../../../common/api/endpoint'; import type { ResponseActionsClientOptions, @@ -361,7 +362,7 @@ export class SentinelOneActionsClient extends ResponseActionsClientImpl { } async release( - actionRequest: IsolationRouteRequestBody, + actionRequest: UnisolationRouteRequestBody, options: CommonResponseActionMethodOptions = {} ): Promise<ActionDetails> { const reqIndexOptions: ResponseActionsClientWriteActionRequestToEndpointIndexOptions< diff --git a/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts b/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts index d45e59b2fe295..558f7e7ade2f6 100644 --- a/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts +++ b/x-pack/plugins/security_solution/server/integration_tests/telemetry.test.ts @@ -148,8 +148,7 @@ describe('telemetry tasks', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/187719 - describe.skip('detection-rules', () => { + describe('detection-rules', () => { it('should execute when scheduled', async () => { await mockAndScheduleDetectionRulesTask(); @@ -169,8 +168,7 @@ describe('telemetry tasks', () => { }); it('should send task metrics', async () => { - const task = await mockAndScheduleDetectionRulesTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleDetectionRulesTask(); const requests = await getTaskMetricsRequests(task, started); @@ -181,13 +179,10 @@ describe('telemetry tasks', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/178918 - // FLAKY: https://github.com/elastic/kibana/issues/187720 - describe.skip('sender configuration', () => { + describe('sender configuration', () => { it('should use legacy sender by default', async () => { // launch a random task and verify it uses the new configuration - const task = await mockAndScheduleDetectionRulesTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleDetectionRulesTask(); const requests = await getTaskMetricsRequests(task, started); expect(requests.length).toBeGreaterThan(0); @@ -216,8 +211,7 @@ describe('telemetry tasks', () => { expect(found).toBeFalsy(); }); - const task = await mockAndScheduleDetectionRulesTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleDetectionRulesTask(); const requests = await getTaskMetricsRequests(task, started); expect(requests.length).toBeGreaterThan(0); @@ -258,8 +252,7 @@ describe('telemetry tasks', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/189192 - describe.skip('endpoint-diagnostics', () => { + describe('endpoint-diagnostics', () => { it('should execute when scheduled', async () => { await mockAndScheduleEndpointDiagnosticsTask(); @@ -298,8 +291,7 @@ describe('telemetry tasks', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/189330 - describe.skip('endpoint-meta-telemetry', () => { + describe('endpoint-meta-telemetry', () => { beforeEach(async () => { await initEndpointIndices(esClient); }); @@ -335,8 +327,7 @@ describe('telemetry tasks', () => { Promise.reject(Error(errorMessage)) ); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const requests = await getTaskMetricsRequests(task, started); @@ -361,8 +352,7 @@ describe('telemetry tasks', () => { agentClient.listAgents = jest.fn((_) => Promise.reject(Error(errorMessage))); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -401,8 +391,7 @@ describe('telemetry tasks', () => { }) ); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -434,8 +423,7 @@ describe('telemetry tasks', () => { telemetryReceiver.fetchPolicyConfigs = jest.fn((_) => Promise.reject(Error(errorMessage))); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -478,8 +466,7 @@ describe('telemetry tasks', () => { } as unknown as AgentPolicy); }); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -512,8 +499,7 @@ describe('telemetry tasks', () => { return Promise.reject(Error(errorMessage)); }); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -545,8 +531,7 @@ describe('telemetry tasks', () => { return Promise.resolve(new Map()); }); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -579,8 +564,7 @@ describe('telemetry tasks', () => { return Promise.reject(Error(errorMessage)); }); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -615,8 +599,7 @@ describe('telemetry tasks', () => { return Promise.resolve(new Map()); }); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -663,8 +646,7 @@ describe('telemetry tasks', () => { return esClient.search(query); }); - const task = await mockAndScheduleEndpointTask(); - const started = performance.now(); + const [task, started] = await mockAndScheduleEndpointTask(); const endpointMetaRequests = await getEndpointMetaRequests(); @@ -688,8 +670,7 @@ describe('telemetry tasks', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/188234 - describe.skip('telemetry-prebuilt-rule-alerts', () => { + describe('telemetry-prebuilt-rule-alerts', () => { it('should execute when scheduled', async () => { await mockAndSchedulePrebuiltRulesTask(); @@ -722,8 +703,7 @@ describe('telemetry tasks', () => { telemetryReceiver.fetchPrebuiltRuleAlertsBatch = mockedGenerator; - const task = await mockAndSchedulePrebuiltRulesTask(); - const started = performance.now(); + const [task, started] = await mockAndSchedulePrebuiltRulesTask(); const requests = await getTaskMetricsRequests(task, started); @@ -781,7 +761,7 @@ describe('telemetry tasks', () => { }); } - async function mockAndScheduleDetectionRulesTask(): Promise<SecurityTelemetryTask> { + async function mockAndScheduleDetectionRulesTask(): Promise<[SecurityTelemetryTask, number]> { const task = getTelemetryTask(tasks, 'security:telemetry-detection-rules'); // create some data @@ -797,50 +777,52 @@ describe('telemetry tasks', () => { exceptionsListItem.push(exceptionListItem); // schedule task to run ASAP - await eventually(async () => { + return eventually(async () => { + const started = performance.now(); await taskManagerPlugin.runSoon(task.getTaskId()); + return [task, started]; }); - - return task; } - async function mockAndScheduleEndpointTask(): Promise<SecurityTelemetryTask> { + async function mockAndScheduleEndpointTask(): Promise<[SecurityTelemetryTask, number]> { const task = getTelemetryTask(tasks, 'security:endpoint-meta-telemetry'); await mockEndpointData(esClient, kibanaServer.coreStart.savedObjects); // schedule task to run ASAP - await eventually(async () => { + return eventually(async () => { + const started = performance.now(); await taskManagerPlugin.runSoon(task.getTaskId()); + return [task, started]; }); - - return task; } - async function mockAndSchedulePrebuiltRulesTask(): Promise<SecurityTelemetryTask> { + async function mockAndSchedulePrebuiltRulesTask(): Promise<[SecurityTelemetryTask, number]> { const task = getTelemetryTask(tasks, 'security:telemetry-prebuilt-rule-alerts'); await mockPrebuiltRulesData(esClient); // schedule task to run ASAP - await eventually(async () => { + return eventually(async () => { + const started = performance.now(); await taskManagerPlugin.runSoon(task.getTaskId()); + return [task, started]; }); - - return task; } - async function mockAndScheduleEndpointDiagnosticsTask(): Promise<SecurityTelemetryTask> { + async function mockAndScheduleEndpointDiagnosticsTask(): Promise< + [SecurityTelemetryTask, number] + > { const task = getTelemetryTask(tasks, 'security:endpoint-diagnostics'); await createMockedEndpointAlert(kibanaServer.coreStart.elasticsearch.client.asInternalUser); // schedule task to run ASAP - await eventually(async () => { + return eventually(async () => { + const started = performance.now(); await taskManagerPlugin.runSoon(task.getTaskId()); + return [task, started]; }); - - return task; } function mockAxiosGet(bufferConfig: unknown = fakeBufferAndSizesConfigAsyncDisabled) { @@ -877,6 +859,7 @@ describe('telemetry tasks', () => { requestConfig: AxiosRequestConfig<unknown> | undefined; }> > { + const taskType = getTelemetryTaskType(task); return eventually(async () => { const calls = mockedAxiosPost.mock.calls.flatMap(([url, data, config]) => { return (data as string).split('\n').map((body) => { @@ -886,20 +869,24 @@ describe('telemetry tasks', () => { const requests = calls.filter(({ url, body }) => { return ( - body.indexOf(getTelemetryTaskType(task)) !== -1 && + body.indexOf(taskType) !== -1 && url.startsWith(ENDPOINT_STAGING) && url.endsWith('task-metrics') ); }); expect(requests.length).toBeGreaterThan(0); - return requests + const filtered = requests .map((r) => { return { taskMetric: JSON.parse(r.body) as TaskMetric, requestConfig: r.config, }; }) - .filter((t) => t.taskMetric.start_time >= olderThan); + .filter((t) => { + return t.taskMetric.start_time >= olderThan; + }); + expect(filtered.length).toBeGreaterThan(0); + return filtered; }); } }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.test.ts new file mode 100644 index 0000000000000..c2982d4b16092 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.test.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 { bootstrapPrebuiltRulesRoute } from './bootstrap_prebuilt_rules'; + +import type { Installation, RegistryPackage } from '@kbn/fleet-plugin/common'; +import { requestContextMock, serverMock } from '../../../routes/__mocks__'; +import { getBootstrapRulesRequest } from '../../../routes/__mocks__/request_responses'; + +const packageMock: RegistryPackage = { + name: 'detection_engine', + version: '1.0.0', + format_version: '1.0.0', + title: 'Test package', + description: 'Test package', + owner: { github: 'elastic' }, + download: '', + path: '', +}; + +const installationMock: Installation = { + name: 'detection_engine', + version: '1.0.0', + installed_kibana: [], + installed_es: [], + es_index_patterns: {}, + install_status: 'installed', + verification_status: 'verified', + install_version: '1.0.0', + install_source: 'registry', + install_started_at: '2021-08-02T15:00:00.000Z', +}; + +describe('bootstrap_prebuilt_rules_route', () => { + let server: ReturnType<typeof serverMock.create>; + let { clients, context } = requestContextMock.createTools(); + + beforeEach(() => { + jest.clearAllMocks(); + server = serverMock.create(); + ({ clients, context } = requestContextMock.createTools()); + + bootstrapPrebuiltRulesRoute(server.router); + }); + + it('returns information about installed packages', async () => { + clients.internalFleetServices.packages.fetchFindLatestPackage.mockResolvedValue(packageMock); + clients.internalFleetServices.packages.ensureInstalledPackage.mockResolvedValue({ + status: 'installed', + package: installationMock, + }); + const response = await server.inject( + getBootstrapRulesRequest(), + requestContextMock.convertContext(context) + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + packages: expect.arrayContaining([ + expect.objectContaining({ + name: 'detection_engine', + version: '1.0.0', + status: 'installed', + }), + ]), + }); + }); + + it('installs pre-release packages in dev mode', async () => { + // Mock the package installation + clients.internalFleetServices.packages.fetchFindLatestPackage.mockResolvedValue(packageMock); + clients.internalFleetServices.packages.ensureInstalledPackage.mockResolvedValue({ + status: 'installed', + package: installationMock, + }); + + // Mock Kibana build and branch + clients.appClient.getBuildFlavor.mockReturnValue('traditional'); + clients.appClient.getKibanaBranch.mockReturnValue('main'); + + const response = await server.inject( + getBootstrapRulesRequest(), + requestContextMock.convertContext(context) + ); + expect(response.status).toEqual(200); + expect(clients.internalFleetServices.packages.fetchFindLatestPackage).toHaveBeenCalledWith( + 'security_detection_engine', + { prerelease: true } + ); + }); + + it('installs pre-release packages for release candidates', async () => { + // Mock the package installation + clients.internalFleetServices.packages.fetchFindLatestPackage.mockResolvedValue(packageMock); + clients.internalFleetServices.packages.ensureInstalledPackage.mockResolvedValue({ + status: 'installed', + package: installationMock, + }); + + // Mock Kibana build and branch + clients.appClient.getBuildFlavor.mockReturnValue('traditional'); + clients.appClient.getKibanaBranch.mockReturnValue('8.16.0'); + clients.appClient.getKibanaVersion.mockReturnValue('8.16.0-SNAPSHOT'); + + const response = await server.inject( + getBootstrapRulesRequest(), + requestContextMock.convertContext(context) + ); + expect(response.status).toEqual(200); + expect(clients.internalFleetServices.packages.fetchFindLatestPackage).toHaveBeenCalledWith( + 'security_detection_engine', + { prerelease: true } + ); + }); + + it('installs release packages for Serverless', async () => { + // Mock the package installation + clients.internalFleetServices.packages.fetchFindLatestPackage.mockResolvedValue(packageMock); + clients.internalFleetServices.packages.ensureInstalledPackage.mockResolvedValue({ + status: 'installed', + package: installationMock, + }); + + // Mock Kibana build and branch + clients.appClient.getBuildFlavor.mockReturnValue('serverless'); + clients.appClient.getKibanaBranch.mockReturnValue('main'); + + const response = await server.inject( + getBootstrapRulesRequest(), + requestContextMock.convertContext(context) + ); + expect(response.status).toEqual(200); + expect(clients.internalFleetServices.packages.fetchFindLatestPackage).toHaveBeenCalledWith( + 'security_detection_engine', + { prerelease: false } + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.ts new file mode 100644 index 0000000000000..d17435a543320 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import type { IKibanaResponse } from '@kbn/core/server'; +import { BOOTSTRAP_PREBUILT_RULES_URL } from '../../../../../../common/api/detection_engine/prebuilt_rules'; +import type { BootstrapPrebuiltRulesResponse } from '../../../../../../common/api/detection_engine/prebuilt_rules/bootstrap_prebuilt_rules/bootstrap_prebuilt_rules.gen'; +import type { SecuritySolutionPluginRouter } from '../../../../../types'; +import { buildSiemResponse } from '../../../routes/utils'; +import { + installEndpointPackage, + installPrebuiltRulesPackage, +} from '../install_prebuilt_rules_and_timelines/install_prebuilt_rules_package'; + +export const bootstrapPrebuiltRulesRoute = (router: SecuritySolutionPluginRouter) => { + router.versioned + .post({ + access: 'internal', + path: BOOTSTRAP_PREBUILT_RULES_URL, + options: { + tags: ['access:securitySolution'], + }, + }) + .addVersion( + { + version: '1', + validate: {}, + }, + async (context, _, response): Promise<IKibanaResponse<BootstrapPrebuiltRulesResponse>> => { + const siemResponse = buildSiemResponse(response); + + try { + const ctx = await context.resolve(['securitySolution']); + const securityContext = ctx.securitySolution; + const config = securityContext.getConfig(); + + const results = await Promise.all([ + installPrebuiltRulesPackage(config, securityContext), + installEndpointPackage(config, securityContext), + ]); + + const responseBody: BootstrapPrebuiltRulesResponse = { + packages: results.map((result) => ({ + name: result.package.name, + version: result.package.version, + status: result.status, + })), + }; + + return response.ok({ + body: responseBody, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_package.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_package.ts index 4bbc4c3561674..23bcc654eb780 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_package.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_package.ts @@ -6,7 +6,10 @@ */ import type { SecuritySolutionApiRequestHandlerContext } from '../../../../../types'; -import { PREBUILT_RULES_PACKAGE_NAME } from '../../../../../../common/detection_engine/constants'; +import { + ENDPOINT_PACKAGE_NAME, + PREBUILT_RULES_PACKAGE_NAME, +} from '../../../../../../common/detection_engine/constants'; import type { ConfigType } from '../../../../../config'; /** @@ -19,24 +22,45 @@ export async function installPrebuiltRulesPackage( config: ConfigType, context: SecuritySolutionApiRequestHandlerContext ) { - // Get package version from the config + const pkgVersion = await findLatestPackageVersion(config, context, PREBUILT_RULES_PACKAGE_NAME); + + return context + .getInternalFleetServices() + .packages.ensureInstalledPackage({ pkgName: PREBUILT_RULES_PACKAGE_NAME, pkgVersion }); +} + +export async function installEndpointPackage( + config: ConfigType, + context: SecuritySolutionApiRequestHandlerContext +) { + const pkgVersion = await findLatestPackageVersion(config, context, ENDPOINT_PACKAGE_NAME); + + return context.getInternalFleetServices().packages.ensureInstalledPackage({ + pkgName: ENDPOINT_PACKAGE_NAME, + pkgVersion, + }); +} + +async function findLatestPackageVersion( + config: ConfigType, + context: SecuritySolutionApiRequestHandlerContext, + packageName: string +) { let pkgVersion = config.prebuiltRulesPackageVersion; // Find latest package if the version isn't specified in the config if (!pkgVersion) { + const securityAppClient = context.getAppClient(); // Use prerelease versions in dev environment const isPrerelease = - context.getAppClient().getKibanaVersion().includes('-SNAPSHOT') || - context.getAppClient().getKibanaBranch() === 'main'; + securityAppClient.getBuildFlavor() === 'traditional' && + (securityAppClient.getKibanaVersion().includes('-SNAPSHOT') || + securityAppClient.getKibanaBranch() === 'main'); const result = await context .getInternalFleetServices() - .packages.fetchFindLatestPackage(PREBUILT_RULES_PACKAGE_NAME, { prerelease: isPrerelease }); + .packages.fetchFindLatestPackage(packageName, { prerelease: isPrerelease }); pkgVersion = result.version; } - - // Install the package - await context - .getInternalFleetServices() - .packages.ensureInstalledPackage({ pkgName: PREBUILT_RULES_PACKAGE_NAME, pkgVersion }); + return pkgVersion; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts index 71740086e3fa5..c9871f86a43e2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/register_routes.ts @@ -14,6 +14,7 @@ import { reviewRuleInstallationRoute } from './review_rule_installation/review_r import { reviewRuleUpgradeRoute } from './review_rule_upgrade/review_rule_upgrade_route'; import { performRuleInstallationRoute } from './perform_rule_installation/perform_rule_installation_route'; import { performRuleUpgradeRoute } from './perform_rule_upgrade/perform_rule_upgrade_route'; +import { bootstrapPrebuiltRulesRoute } from './bootstrap_prebuilt_rules/bootstrap_prebuilt_rules'; export const registerPrebuiltRulesRoutes = (router: SecuritySolutionPluginRouter) => { // Legacy endpoints that we're going to deprecate @@ -26,4 +27,5 @@ export const registerPrebuiltRulesRoutes = (router: SecuritySolutionPluginRouter performRuleUpgradeRoute(router); reviewRuleInstallationRoute(router); reviewRuleUpgradeRoute(router); + bootstrapPrebuiltRulesRoute(router); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index fa9fcc1fa5e22..b39cba0cf4952 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -39,6 +39,8 @@ import { riskScoreDataClientMock } from '../../../entity_analytics/risk_score/ri import { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { detectionRulesClientMock } from '../../rule_management/logic/detection_rules_client/__mocks__/detection_rules_client'; +import { packageServiceMock } from '@kbn/fleet-plugin/server/services/epm/package_service.mock'; +import type { EndpointInternalFleetServicesInterface } from '../../../../endpoint/services/fleet'; export const createMockClients = () => { const core = coreMock.createRequestHandlerContext(); @@ -70,6 +72,10 @@ export const createMockClients = () => { riskEngineDataClient: riskEngineDataClientMock.create(), riskScoreDataClient: riskScoreDataClientMock.create(), assetCriticalityDataClient: assetCriticalityDataClientMock.create(), + + internalFleetServices: { + packages: packageServiceMock.createClient(), + }, }; }; @@ -146,10 +152,9 @@ const createSecuritySolutionRequestContextMock = ( getDetectionEngineHealthClient: jest.fn(() => clients.detectionEngineHealthClient), getRuleExecutionLog: jest.fn(() => clients.ruleExecutionLog), getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient), - getInternalFleetServices: jest.fn(() => { - // TODO: Mock EndpointInternalFleetServicesInterface and return the mocked object. - throw new Error('Not implemented'); - }), + getInternalFleetServices: jest.fn( + () => clients.internalFleetServices as unknown as EndpointInternalFleetServicesInterface + ), getRiskEngineDataClient: jest.fn(() => clients.riskEngineDataClient), getRiskScoreDataClient: jest.fn(() => clients.riskScoreDataClient), getAssetCriticalityDataClient: jest.fn(() => clients.assetCriticalityDataClient), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 2bd7efdbbdcf2..3a48bb449c55d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -33,6 +33,7 @@ import { import { RULE_MANAGEMENT_FILTERS_URL } from '../../../../../common/api/detection_engine/rule_management/urls'; import { + BOOTSTRAP_PREBUILT_RULES_URL, PREBUILT_RULES_STATUS_URL, PREBUILT_RULES_URL, } from '../../../../../common/api/detection_engine/prebuilt_rules'; @@ -203,6 +204,12 @@ export const getRuleManagementFiltersRequest = () => path: RULE_MANAGEMENT_FILTERS_URL, }); +export const getBootstrapRulesRequest = () => + requestMock.create({ + method: 'post', + path: BOOTSTRAP_PREBUILT_RULES_URL, + }); + export interface FindHit<T = RuleAlertType> { page: number; perPage: number; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 81685984439cd..e45f2babe94f7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -49,6 +49,7 @@ import { TIMESTAMP_RUNTIME_FIELD } from './constants'; import { buildTimestampRuntimeMapping } from './utils/build_timestamp_runtime_mapping'; import { getFieldsForWildcard } from './utils/get_fields_for_wildcard'; import { alertsFieldMap, rulesFieldMap } from '../../../../common/field_maps'; +import { sendAlertSuppressionTelemetryEvent } from './utils/telemetry/send_alert_suppression_telemetry_event'; const aliasesFieldMap: FieldMap = {}; Object.entries(aadFieldConversion).forEach(([key, value]) => { @@ -80,6 +81,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = isPreview, experimentalFeatures, alerting, + analytics, }) => (type) => { const { alertIgnoreFields: ignoreFields, alertMergeStrategy: mergeStrategy } = config; @@ -448,6 +450,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = enrichmentTimes: result.enrichmentTimes.concat(runResult.enrichmentTimes), createdSignals, createdSignalsCount: createdSignals.length, + suppressedAlertsCount: runResult.suppressedAlertsCount, errors: result.errors.concat(runResult.errors), lastLookbackDate: runResult.lastLookBackDate, searchAfterTimes: result.searchAfterTimes.concat(runResult.searchAfterTimes), @@ -465,6 +468,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = enrichmentTimes: [], createdSignals: [], createdSignalsCount: 0, + suppressedAlertsCount: 0, errors: [], searchAfterTimes: [], state, @@ -549,6 +553,16 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = }); } + if (!isPreview && analytics) { + sendAlertSuppressionTelemetryEvent({ + analytics, + suppressedAlertsCount: result.suppressedAlertsCount ?? 0, + createdAlertsCount: result.createdSignalsCount, + ruleAttributes: rule, + ruleParams: params, + }); + } + return { state: result.state }; }); }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts index 5f19f8f2218f1..b129a7ef0c5bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/esql/esql.ts @@ -196,7 +196,6 @@ export const esqlExecutor = async ({ maxNumberOfAlertsMultiplier: 1, }); - addToSearchAfterReturn({ current: result, next: bulkCreateResult }); ruleExecutionLogger.debug( `Created ${bulkCreateResult.createdItemsCount} alerts. Suppressed ${bulkCreateResult.suppressedItemsCount} alerts` ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts index b1d897250ca4e..38018060e2252 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/alert_suppression/group_and_bulk_create.ts @@ -32,6 +32,7 @@ import type { ITelemetryEventsSender } from '../../../../telemetry/sender'; import { DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY } from '../../../../../../common/detection_engine/constants'; import type { ExperimentalFeatures } from '../../../../../../common'; import { createEnrichEventsFunction } from '../../utils/enrichments'; +import { getNumberOfSuppressedAlerts } from '../../utils/get_number_of_suppressed_alerts'; export interface BucketHistory { key: Record<string, string | number | null>; @@ -274,6 +275,7 @@ export const groupAndBulkCreate = async ({ suppressionWindow, alertTimestampOverride: runOpts.alertTimestampOverride, experimentalFeatures, + ruleType: 'query', }); addToSearchAfterReturn({ current: toReturn, next: bulkCreateResult }); runOpts.ruleExecutionLogger.debug(`created ${bulkCreateResult.createdItemsCount} signals`); @@ -286,7 +288,13 @@ export const groupAndBulkCreate = async ({ logger: runOpts.ruleExecutionLogger, }) ); - addToSearchAfterReturn({ current: toReturn, next: bulkCreateResult }); + addToSearchAfterReturn({ + current: toReturn, + next: { + ...bulkCreateResult, + suppressedItemsCount: getNumberOfSuppressedAlerts(bulkCreateResult.createdItems, []), + }, + }); runOpts.ruleExecutionLogger.debug(`created ${bulkCreateResult.createdItemsCount} signals`); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 8f7a50b195e4f..4069b7782e0e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -11,6 +11,7 @@ import type { Logger } from '@kbn/logging'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { SuppressionFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; +import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import type { QUERY_RULE_TYPE_ID, SAVED_QUERY_RULE_TYPE_ID } from '@kbn/securitysolution-rules'; @@ -72,6 +73,7 @@ export interface SecurityAlertTypeReturnValue<TState extends RuleTypeState> { success: boolean; warning: boolean; warningMessages: string[]; + suppressedAlertsCount?: number; } export interface RunOpts<TParams extends RuleParams> { @@ -139,6 +141,7 @@ export interface CreateSecurityRuleTypeWrapperProps { isPreview?: boolean; experimentalFeatures?: ExperimentalFeatures; alerting: SetupPlugins['alerting']; + analytics?: AnalyticsServiceSetup; } export type CreateSecurityRuleTypeWrapper = ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_with_suppression.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_with_suppression.ts index 75aa46d039277..7decfd8294913 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_with_suppression.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/bulk_create_with_suppression.ts @@ -7,12 +7,14 @@ import { performance } from 'perf_hooks'; import { isEmpty } from 'lodash'; - +import type { Type as RuleType } from '@kbn/securitysolution-io-ts-alerting-types'; import type { SuppressedAlertService } from '@kbn/rule-registry-plugin/server'; import type { AlertWithCommonFieldsLatest, SuppressionFieldsLatest, } from '@kbn/rule-registry-plugin/common/schemas'; + +import { isQueryRule } from '../../../../../common/detection_engine/utils'; import type { IRuleExecutionLogForExecutors } from '../../rule_monitoring'; import { makeFloatString } from './utils'; import type { @@ -22,6 +24,7 @@ import type { import type { RuleServices } from '../types'; import { createEnrichEventsFunction } from './enrichments'; import type { ExperimentalFeatures } from '../../../../../common'; +import { getNumberOfSuppressedAlerts } from './get_number_of_suppressed_alerts'; export interface GenericBulkCreateResponse<T extends BaseFieldsLatest> { success: boolean; @@ -46,6 +49,7 @@ export const bulkCreateWithSuppression = async < isSuppressionPerRuleExecution, maxAlerts, experimentalFeatures, + ruleType, }: { alertWithSuppression: SuppressedAlertService; ruleExecutionLogger: IRuleExecutionLogForExecutors; @@ -56,6 +60,7 @@ export const bulkCreateWithSuppression = async < isSuppressionPerRuleExecution?: boolean; maxAlerts?: number; experimentalFeatures: ExperimentalFeatures; + ruleType?: RuleType; }): Promise<GenericBulkCreateResponse<T>> => { if (wrappedDocs.length === 0) { return { @@ -110,6 +115,15 @@ export const bulkCreateWithSuppression = async < ruleExecutionLogger.debug(`Alerts bulk process took ${makeFloatString(end - start)} ms`); + // query rule type suppression does not happen in memory, so we can't just count createdAlerts and suppressedAlerts + // for this rule type we need to look into alerts suppression properties, extract those values and sum up + const suppressedItemsCount = isQueryRule(ruleType) + ? getNumberOfSuppressedAlerts( + createdAlerts, + suppressedAlerts.map(({ _source, _id }) => ({ _id, ..._source })) + ) + : suppressedAlerts.length; + if (!isEmpty(errors)) { ruleExecutionLogger.warn(`Alerts bulk process finished with errors: ${JSON.stringify(errors)}`); return { @@ -119,7 +133,7 @@ export const bulkCreateWithSuppression = async < bulkCreateDuration: makeFloatString(end - start), createdItemsCount: createdAlerts.length, createdItems: createdAlerts, - suppressedItemsCount: suppressedAlerts.length, + suppressedItemsCount, alertsWereTruncated, }; } else { @@ -130,7 +144,7 @@ export const bulkCreateWithSuppression = async < enrichmentDuration: makeFloatString(enrichmentsTimeFinish - enrichmentsTimeStart), createdItemsCount: createdAlerts.length, createdItems: createdAlerts, - suppressedItemsCount: suppressedAlerts.length, + suppressedItemsCount, alertsWereTruncated, }; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_number_of_suppressed_alerts.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_number_of_suppressed_alerts.test.ts new file mode 100644 index 0000000000000..c817c58679899 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_number_of_suppressed_alerts.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getNumberOfSuppressedAlerts } from './get_number_of_suppressed_alerts'; +import { ALERT_SUPPRESSION_DOCS_COUNT } from '@kbn/rule-data-utils'; +import type { SuppressionFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; + +import type { BaseFieldsLatest } from '../../../../../common/api/detection_engine/model/alerts'; +describe('getNumberOfSuppressedAlerts', () => { + it('should count total number of suppressed alerts in created alerts', () => { + const createdAlerts = [ + { _id: '1', [ALERT_SUPPRESSION_DOCS_COUNT]: 2 }, + { _id: '2', [ALERT_SUPPRESSION_DOCS_COUNT]: 0 }, + { _id: '3', [ALERT_SUPPRESSION_DOCS_COUNT]: 4 }, + { _id: '4' }, + ] as Array<SuppressionFieldsLatest & BaseFieldsLatest & { _id: string }>; + + expect(getNumberOfSuppressedAlerts(createdAlerts, [])).toBe(6); + }); + + // each alert in suppressed alerts counts as +1, since it is also suppressed + it('should count total number of suppressed alerts in suppressed alerts', () => { + const suppressedAlerts = [ + { _id: '1', [ALERT_SUPPRESSION_DOCS_COUNT]: 2 }, + { _id: '2', [ALERT_SUPPRESSION_DOCS_COUNT]: 0 }, + { _id: '3', [ALERT_SUPPRESSION_DOCS_COUNT]: 4 }, + ] as Array<SuppressionFieldsLatest & BaseFieldsLatest & { _id: string }>; + + expect(getNumberOfSuppressedAlerts([], suppressedAlerts)).toBe(9); + }); + + it('should count total number of suppressed alerts', () => { + const createdAlerts = [{ _id: '1', [ALERT_SUPPRESSION_DOCS_COUNT]: 2 }] as Array< + SuppressionFieldsLatest & BaseFieldsLatest & { _id: string } + >; + const suppressedAlerts = [{ _id: '3', [ALERT_SUPPRESSION_DOCS_COUNT]: 4 }] as Array< + SuppressionFieldsLatest & BaseFieldsLatest & { _id: string } + >; + expect(getNumberOfSuppressedAlerts(createdAlerts, suppressedAlerts)).toBe(7); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_number_of_suppressed_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_number_of_suppressed_alerts.ts new file mode 100644 index 0000000000000..7b4615b56509d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/get_number_of_suppressed_alerts.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ALERT_SUPPRESSION_DOCS_COUNT } from '@kbn/rule-data-utils'; +import type { SuppressionFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; + +import type { BaseFieldsLatest } from '../../../../../common/api/detection_engine/model/alerts'; + +export const getNumberOfSuppressedAlerts = < + T extends SuppressionFieldsLatest & BaseFieldsLatest & { _id: string } +>( + createdAlerts: T[], + suppressedAlerts: T[] +): number => { + return ( + createdAlerts.reduce((acc, alert) => acc + (alert?.[ALERT_SUPPRESSION_DOCS_COUNT] || 0), 0) + + suppressedAlerts.reduce( + (acc, alert) => acc + (alert?.[ALERT_SUPPRESSION_DOCS_COUNT] || 0) + 1, + 0 + ) + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts new file mode 100644 index 0000000000000..c7eecaa5ce076 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.test.ts @@ -0,0 +1,202 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { coreMock } from '@kbn/core/server/mocks'; +import type { AnalyticsServiceSetup } from '@kbn/core/public'; +import type { RuleParams } from '../../../rule_schema'; +import type { SanitizedRuleConfig } from '@kbn/alerting-plugin/common'; +import { ALERT_SUPPRESSION_EVENT } from '../../../../telemetry/event_based/events'; + +import { + sendAlertSuppressionTelemetryEvent, + suppressionDurationToSeconds, +} from './send_alert_suppression_telemetry_event'; + +describe('suppressionDurationToSeconds', () => { + it('should convert hours to seconds', () => { + expect(suppressionDurationToSeconds({ value: 5, unit: 'h' })).toBe(18000); + }); + it('should convert minutes to seconds', () => { + expect(suppressionDurationToSeconds({ value: 23, unit: 'm' })).toBe(1380); + }); + it('should return seconds', () => { + expect(suppressionDurationToSeconds({ value: 5, unit: 's' })).toBe(5); + }); + it('should return -1 when duration is undefined', () => { + expect(suppressionDurationToSeconds(undefined)).toBe(-1); + }); +}); + +describe('sendAlertSuppressionTelemetryEvent', () => { + let mockAnalytics: jest.Mocked<AnalyticsServiceSetup>; + let mockCore: ReturnType<typeof coreMock.createSetup>; + const ruleAttributes = { name: 'Detects suspicious activity on endpoint' } as SanitizedRuleConfig; + beforeEach(() => { + mockCore = coreMock.createSetup(); + mockAnalytics = mockCore.analytics; + }); + + it('should not report event if suppression is not configured', () => { + const ruleParams = { + type: 'query', + immutable: false, + ruleId: 'test-rule-id', + } as RuleParams; + + sendAlertSuppressionTelemetryEvent({ + analytics: mockAnalytics, + suppressedAlertsCount: 4, + createdAlertsCount: 4, + ruleParams, + ruleAttributes, + }); + + expect(mockAnalytics.reportEvent).not.toHaveBeenCalled(); + }); + it('should not report event if no alerts created or suppressed', () => { + const ruleParams = { + type: 'query', + immutable: false, + alertSuppression: { + groupBy: ['host.name'], + }, + } as RuleParams; + + sendAlertSuppressionTelemetryEvent({ + analytics: mockAnalytics, + suppressedAlertsCount: 0, + createdAlertsCount: 0, + ruleParams, + ruleAttributes, + }); + + expect(mockAnalytics.reportEvent).not.toHaveBeenCalled(); + }); + it('should report correct event data for threshold rule type', () => { + const ruleParams = { + type: 'threshold', + immutable: false, + alertSuppression: { + duration: { + unit: 'm', + value: 20, + }, + }, + ruleId: 'test-rule-id', + } as RuleParams; + + sendAlertSuppressionTelemetryEvent({ + analytics: mockAnalytics, + suppressedAlertsCount: 1, + createdAlertsCount: 6, + ruleParams, + ruleAttributes, + }); + + expect(mockAnalytics.reportEvent).toHaveBeenCalledWith(ALERT_SUPPRESSION_EVENT.eventType, { + suppressionAlertsCreated: 6, + suppressionAlertsSuppressed: 1, + suppressionDuration: 1200, + suppressionMissingFields: false, + suppressionRuleName: 'Custom rule', + suppressionRuleType: 'threshold', + suppressionGroupByFields: [], + suppressionGroupByFieldsNumber: 0, + suppressionRuleId: 'test-rule-id', + }); + }); + it('should report correct event data for query rule type with per time period suppression', () => { + const ruleParams = { + type: 'query', + immutable: false, + alertSuppression: { + groupBy: ['host.name'], + duration: { + unit: 'h', + value: 1, + }, + }, + ruleId: 'test-rule-id', + } as RuleParams; + + sendAlertSuppressionTelemetryEvent({ + analytics: mockAnalytics, + suppressedAlertsCount: 0, + createdAlertsCount: 10, + ruleParams, + ruleAttributes, + }); + + expect(mockAnalytics.reportEvent).toHaveBeenCalledWith(ALERT_SUPPRESSION_EVENT.eventType, { + suppressionAlertsCreated: 10, + suppressionAlertsSuppressed: 0, + suppressionDuration: 3600, + suppressionMissingFields: true, + suppressionRuleType: 'query', + suppressionRuleName: 'Custom rule', + suppressionGroupByFields: ['host.name'], + suppressionGroupByFieldsNumber: 1, + suppressionRuleId: 'test-rule-id', + }); + }); + + it('should report correct event data for esql rule type with per execution suppression', () => { + const ruleParams = { + type: 'esql', + immutable: false, + alertSuppression: { + groupBy: ['host.name', 'host.ip'], + missingFieldsStrategy: 'doNotSuppress', + }, + ruleId: 'test-rule-id', + } as RuleParams; + + sendAlertSuppressionTelemetryEvent({ + analytics: mockAnalytics, + suppressedAlertsCount: 2, + createdAlertsCount: 11, + ruleParams, + ruleAttributes, + }); + + expect(mockAnalytics.reportEvent).toHaveBeenCalledWith(ALERT_SUPPRESSION_EVENT.eventType, { + suppressionAlertsCreated: 11, + suppressionAlertsSuppressed: 2, + suppressionDuration: -1, + suppressionMissingFields: false, + suppressionRuleName: 'Custom rule', + suppressionRuleType: 'esql', + suppressionGroupByFields: ['host.name', 'host.ip'], + suppressionGroupByFieldsNumber: 2, + suppressionRuleId: 'test-rule-id', + }); + }); + it('should report prebuilt rule name', () => { + const ruleParams = { + type: 'esql', + immutable: true, + alertSuppression: { + groupBy: ['host.name', 'host.ip'], + missingFieldsStrategy: 'doNotSuppress', + }, + } as RuleParams; + + sendAlertSuppressionTelemetryEvent({ + analytics: mockAnalytics, + suppressedAlertsCount: 2, + createdAlertsCount: 11, + ruleParams, + ruleAttributes, + }); + + expect(mockAnalytics.reportEvent).toHaveBeenCalledWith( + ALERT_SUPPRESSION_EVENT.eventType, + expect.objectContaining({ + suppressionRuleName: 'Detects suspicious activity on endpoint', + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.ts new file mode 100644 index 0000000000000..ee597c59f3246 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/telemetry/send_alert_suppression_telemetry_event.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; +import type { SanitizedRuleConfig } from '@kbn/alerting-plugin/common'; +import { ALERT_SUPPRESSION_EVENT } from '../../../../telemetry/event_based/events'; +import type { AlertSuppressionDuration } from '../../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; +import type { RuleParams } from '../../../rule_schema'; + +import { isThresholdParams } from '../utils'; + +export const suppressionDurationToSeconds = ( + duration: AlertSuppressionDuration | undefined +): number => { + if (!duration) { + return -1; + } + switch (duration.unit) { + case 's': + return duration.value; + case 'm': + return duration.value * 60; + case 'h': + return duration.value * 60 * 60; + default: + return -1; + } +}; + +interface SendAlertSuppressionEventArgs { + analytics: AnalyticsServiceSetup; + suppressedAlertsCount: number; + createdAlertsCount: number; + ruleParams: RuleParams; + ruleAttributes: SanitizedRuleConfig; +} + +export const sendAlertSuppressionTelemetryEvent = ({ + analytics, + suppressedAlertsCount, + createdAlertsCount, + ruleParams, + ruleAttributes, +}: SendAlertSuppressionEventArgs): void => { + // do not send any telemetry event if suppression is not configured + if (ruleParams.alertSuppression == null) { + return; + } + + // do not send any telemetry if no alerts were suppressed or created + if (suppressedAlertsCount + createdAlertsCount === 0) { + return; + } + + const suppressionGroupByFieldsNumber = isThresholdParams(ruleParams) + ? ruleParams.threshold?.field?.length || 0 + : ruleParams.alertSuppression.groupBy.length; + + const suppressionGroupByFields = isThresholdParams(ruleParams) + ? ruleParams.threshold?.field || [] + : ruleParams.alertSuppression.groupBy; + + const suppressionMissingFields = isThresholdParams(ruleParams) + ? false + : ruleParams.alertSuppression.missingFieldsStrategy !== 'doNotSuppress'; + + const telemetryEvent = { + suppressionAlertsCreated: createdAlertsCount, + suppressionAlertsSuppressed: suppressedAlertsCount, + suppressionRuleName: ruleParams.immutable ? ruleAttributes.name : 'Custom rule', + suppressionDuration: suppressionDurationToSeconds(ruleParams.alertSuppression.duration), + suppressionGroupByFieldsNumber, + suppressionGroupByFields, + suppressionRuleType: ruleParams.type, + suppressionMissingFields, + suppressionRuleId: ruleParams.ruleId, + }; + analytics.reportEvent(ALERT_SUPPRESSION_EVENT.eventType, telemetryEvent); +}; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts b/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts index 8eb46b2046c10..e367f012ad0bc 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/event_based/events.ts @@ -123,6 +123,86 @@ export const ASSET_CRITICALITY_SYSTEM_PROCESSED_ASSIGNMENT_FILE_EVENT: EventType }, }; +export const ALERT_SUPPRESSION_EVENT: EventTypeOpts<{ + suppressionAlertsCreated: number; + suppressionAlertsSuppressed: number; + suppressionRuleName: string; + suppressionDuration: number; + suppressionGroupByFieldsNumber: number; + suppressionGroupByFields: string[]; + suppressionRuleType: string; + suppressionMissingFields: boolean; + suppressionRuleId: string; +}> = { + eventType: 'alert_suppression_on_rule_execution', + schema: { + suppressionAlertsCreated: { + type: 'long', + _meta: { + description: + 'Number of alerts created during rule execution with configured alert suppression', + }, + }, + suppressionAlertsSuppressed: { + type: 'long', + _meta: { + description: + 'Number of alerts suppressed during rule execution with configured alert suppression', + }, + }, + suppressionRuleName: { + type: 'keyword', + _meta: { + description: 'Name of rule', + }, + }, + suppressionDuration: { + type: 'long', + _meta: { + description: 'Duration in seconds of suppression period. -1 for per rule execution config', + }, + }, + suppressionGroupByFieldsNumber: { + type: 'long', + _meta: { + description: 'Number of Suppress by fields', + }, + }, + suppressionGroupByFields: { + type: 'array', + items: { + type: 'keyword', + _meta: { + description: 'Tag attached to the element...', + optional: false, + }, + }, + _meta: { + description: 'List of tags attached to the element...', + optional: false, + }, + }, + suppressionRuleType: { + type: 'keyword', + _meta: { + description: 'Rule type', + }, + }, + suppressionMissingFields: { + type: 'boolean', + _meta: { + description: 'Suppression of missing fields enabled', + }, + }, + suppressionRuleId: { + type: 'keyword', + _meta: { + description: 'ruleId', + }, + }, + }, +}; + interface CreateAssetCriticalityProcessedFileEvent { result?: BulkUpsertAssetCriticalityRecordsResponse['stats']; startTime: Date; @@ -175,4 +255,5 @@ export const events = [ RISK_SCORE_EXECUTION_ERROR_EVENT, RISK_SCORE_EXECUTION_CANCELLATION_EVENT, ASSET_CRITICALITY_SYSTEM_PROCESSED_ASSIGNMENT_FILE_EVENT, + ALERT_SUPPRESSION_EVENT, ]; diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.test.ts index 93d7794c059d1..db62235ea592f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.test.ts @@ -13,10 +13,10 @@ import { requestMock, } from '../../../detection_engine/routes/__mocks__'; import { NOTE_URL } from '../../../../../common/constants'; -import type { getNotesPaginated } from '../../utils/common'; +import type { getNotesSchema } from '../../../../../common/api/timeline'; import { mockGetCurrentUser } from '../../__mocks__/import_timelines'; -const getAllNotesRequest = (query?: typeof getNotesPaginated) => +const getAllNotesRequest = (query?: typeof getNotesSchema) => requestMock.create({ method: 'get', path: NOTE_URL, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts index f230d0832a96c..8e8a88a4a6aa9 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts @@ -6,13 +6,16 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; +import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { NOTE_URL } from '../../../../../common/constants'; import type { ConfigType } from '../../../..'; import { buildSiemResponse } from '../../../detection_engine/routes/utils'; -import { buildFrameworkRequest, getNotesPaginated } from '../../utils/common'; +import { buildFrameworkRequest } from '../../utils/common'; +import { getNotesSchema } from '../../../../../common/api/timeline'; +import { buildRouteValidationWithExcess } from '../../../../utils/build_validation/route_validation'; import { getAllSavedNote, MAX_UNASSOCIATED_NOTES } from '../../saved_object/notes'; import { noteSavedObjectType } from '../../saved_object_mappings/notes'; @@ -28,7 +31,7 @@ export const getNotesRoute = (router: SecuritySolutionPluginRouter, _: ConfigTyp .addVersion( { validate: { - request: { query: getNotesPaginated }, + request: { query: buildRouteValidationWithExcess(getNotesSchema) }, }, version: '2023-10-31', }, @@ -63,9 +66,9 @@ export const getNotesRoute = (router: SecuritySolutionPluginRouter, _: ConfigTyp } else { const perPage = queryParams?.perPage ? parseInt(queryParams.perPage, 10) : 10; const page = queryParams?.page ? parseInt(queryParams.page, 10) : 1; - const search = queryParams?.search; - const sortField = queryParams?.sortField; - const sortOrder = queryParams?.sortOrder; + const search = queryParams?.search ?? undefined; + const sortField = queryParams?.sortField ?? undefined; + const sortOrder = (queryParams?.sortOrder as SortOrder) ?? undefined; const filter = queryParams?.filter; const options = { type: noteSavedObjectType, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts index cfa804b848fcd..edee5ee4cd912 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts @@ -38,16 +38,6 @@ export const buildFrameworkRequest = async ( export const escapeHatch = schema.object({}, { unknowns: 'allow' }); -export const getNotesPaginated = schema.object({ - documentIds: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])), - page: schema.maybe(schema.string()), - perPage: schema.maybe(schema.string()), - search: schema.maybe(schema.string()), - sortField: schema.maybe(schema.string()), - sortOrder: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), - filter: schema.maybe(schema.string()), -}); - type ErrorFactory = (message: string) => Error; export const throwErrors = (createError: ErrorFactory) => (errors: rt.Errors) => { diff --git a/x-pack/plugins/security_solution/server/mocks.ts b/x-pack/plugins/security_solution/server/mocks.ts index 7e71246fd1c2c..1d838476a8325 100644 --- a/x-pack/plugins/security_solution/server/mocks.ts +++ b/x-pack/plugins/security_solution/server/mocks.ts @@ -18,6 +18,9 @@ const createAppClientMock = (): AppClientMock => getAlertsIndex: jest.fn(), getSignalsIndex: jest.fn(), getSourcererDataViewId: jest.fn().mockReturnValue('security-solution'), + getKibanaVersion: jest.fn().mockReturnValue('8.0.0'), + getKibanaBranch: jest.fn().mockReturnValue('main'), + getBuildFlavor: jest.fn().mockReturnValue('traditional'), } as unknown as AppClientMock); export const siemMock = { diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 0afd5bbfe6057..c24e70baa5db8 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -197,7 +197,9 @@ export class Plugin implements ISecuritySolutionPlugin { initUiSettings(core.uiSettings, experimentalFeatures, config.enableUiSettingsValidations); productFeaturesService.init(plugins.features); - events.forEach((eventConfig) => core.analytics.registerEventType(eventConfig)); + events.forEach((eventConfig) => { + core.analytics.registerEventType(eventConfig); + }); this.ruleMonitoringService.setup(core, plugins); @@ -222,6 +224,7 @@ export class Plugin implements ISecuritySolutionPlugin { ruleMonitoringService: this.ruleMonitoringService, kibanaVersion: pluginContext.env.packageInfo.version, kibanaBranch: pluginContext.env.packageInfo.branch, + buildFlavor: pluginContext.env.packageInfo.buildFlavor, }); productFeaturesService.registerApiAccessControl(core.http); @@ -305,6 +308,7 @@ export class Plugin implements ISecuritySolutionPlugin { version: pluginContext.env.packageInfo.version, experimentalFeatures: config.experimentalFeatures, alerting: plugins.alerting, + analytics: core.analytics, }; const queryRuleAdditionalOptions: CreateQueryRuleAdditionalOptions = { @@ -414,6 +418,7 @@ export class Plugin implements ISecuritySolutionPlugin { config, kibanaVersion: pluginContext.env.packageInfo.version, kibanaBranch: pluginContext.env.packageInfo.branch, + buildFlavor: pluginContext.env.packageInfo.buildFlavor, }); const endpointFieldsStrategy = endpointFieldsProvider( diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index 780b69681caf4..4bda7e0338aa8 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -9,6 +9,7 @@ import { memoize } from 'lodash'; import type { Logger, KibanaRequest, RequestHandlerContext } from '@kbn/core/server'; +import type { BuildFlavor } from '@kbn/config'; import { DEFAULT_SPACE_ID } from '../common/constants'; import { AppClientFactory } from './client'; import type { ConfigType } from './config'; @@ -47,6 +48,7 @@ interface ConstructorOptions { ruleMonitoringService: IRuleMonitoringService; kibanaVersion: string; kibanaBranch: string; + buildFlavor: BuildFlavor; } export class RequestContextFactory implements IRequestContextFactory { @@ -79,6 +81,7 @@ export class RequestContextFactory implements IRequestContextFactory { config, kibanaVersion: options.kibanaVersion, kibanaBranch: options.kibanaBranch, + buildFlavor: options.buildFlavor, }); const getAuditLogger = () => security?.audit.asScoped(request); diff --git a/x-pack/plugins/security_solution/server/usage/collector.ts b/x-pack/plugins/security_solution/server/usage/collector.ts index 8aab08ea621d8..ca016d07d5099 100644 --- a/x-pack/plugins/security_solution/server/usage/collector.ts +++ b/x-pack/plugins/security_solution/server/usage/collector.ts @@ -78,6 +78,68 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled query rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: 'Number of disabled query rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: 'Number of query rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: 'Number of query rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: 'Number of query rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: + 'Number of query rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of query rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of query rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of query rules configured do not suppress alerts with missing fields', + }, + }, + }, }, threshold: { enabled: { @@ -121,6 +183,71 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled threshold rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: 'Number of disabled threshold rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: + 'Number of threshold rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: + 'Number of threshold rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: + 'Number of threshold rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: + 'Number of threshold rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of threshold rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of threshold rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of threshold rules configured do not suppress alerts with missing fields', + }, + }, + }, }, eql: { enabled: { type: 'long', _meta: { description: 'Number of eql rules enabled' } }, @@ -156,6 +283,67 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled eql rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: 'Number of disabled eql rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: 'Number of eql rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: 'Number of eql rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: 'Number of eql rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: 'Number of eql rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of eql rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of eql rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of eql rules configured do not suppress alerts with missing fields', + }, + }, + }, }, machine_learning: { enabled: { @@ -199,6 +387,73 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: + 'Number of enabled machine_learning rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: + 'Number of disabled machine_learning rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: + 'Number of machine_learning rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: + 'Number of machine_learning rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: + 'Number of machine_learning rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: + 'Number of machine_learning rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of machine_learning rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of machine_learning rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of machine_learning rules configured do not suppress alerts with missing fields', + }, + }, + }, }, threat_match: { enabled: { @@ -242,6 +497,72 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled threat_match rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: + 'Number of disabled threat_match rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: + 'Number of threat_match rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: + 'Number of threat_match rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: + 'Number of threat_match rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: + 'Number of threat_match rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of threat_match rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of threat_match rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of threat_match rules configured do not suppress alerts with missing fields', + }, + }, + }, }, new_terms: { enabled: { @@ -285,6 +606,71 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled new_terms rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: 'Number of disabled new_terms rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: + 'Number of new_terms rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: + 'Number of new_terms rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: + 'Number of new_terms rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: + 'Number of new_terms rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of new_terms rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of new_terms rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of new_terms rules configured do not suppress alerts with missing fields', + }, + }, + }, }, esql: { enabled: { @@ -328,6 +714,67 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled esql rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: 'Number of disabled esql rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: 'Number of esql rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: 'Number of esql rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: 'Number of esql rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: 'Number of esql rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of esql rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of esql rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of esql rules configured do not suppress alerts with missing fields', + }, + }, + }, }, elastic_total: { enabled: { type: 'long', _meta: { description: 'Number of elastic rules enabled' } }, @@ -366,6 +813,69 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled elastic rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: 'Number of disabled elastic rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: 'Number of elastic rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: 'Number of elastic rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: + 'Number of elastic rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: + 'Number of elastic rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of elastic rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of elastic rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of elastic rules configured do not suppress alerts with missing fields', + }, + }, + }, }, custom_total: { enabled: { type: 'long', _meta: { description: 'Number of custom rules enabled' } }, @@ -401,6 +911,68 @@ export const registerCollector: RegisterCollector = ({ 'Number of rules using the legacy investigation fields type introduced only in 8.10 ESS', }, }, + alert_suppression: { + enabled: { + type: 'long', + _meta: { + description: 'Number of enabled custom rules configured with suppression', + }, + }, + disabled: { + type: 'long', + _meta: { + description: 'Number of disabled custom rules configured with suppression', + }, + }, + suppressed_fields_count: { + one: { + type: 'long', + _meta: { + description: 'Number of custom rules configured with one suppression field', + }, + }, + two: { + type: 'long', + _meta: { + description: 'Number of custom rules configured with two suppression field', + }, + }, + three: { + type: 'long', + _meta: { + description: 'Number of custom rules configured with three suppression field', + }, + }, + }, + suppressed_per_time_period: { + type: 'long', + _meta: { + description: + 'Number of custom rules configured with suppression per time period', + }, + }, + suppressed_per_rule_execution: { + type: 'long', + _meta: { + description: + 'Number of custom rules configured with suppression per rule execution', + }, + }, + suppresses_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of custom rules configured to suppress alerts with missing fields', + }, + }, + does_not_suppress_missing_fields: { + type: 'long', + _meta: { + description: + 'Number of custom rules configured do not suppress alerts with missing fields', + }, + }, + }, }, }, detection_rule_detail: { diff --git a/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts b/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts index 6c4d3c5a377d9..be5044fbb4e21 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/get_metrics.test.ts @@ -34,7 +34,7 @@ import { } from './rules/get_metrics.mocks'; import { getInitialDetectionMetrics } from './get_initial_usage'; import { getDetectionsMetrics } from './get_metrics'; -import { getInitialRulesUsage } from './rules/get_initial_usage'; +import { getInitialRulesUsage, initialAlertSuppression } from './rules/get_initial_usage'; describe('Detections Usage and Metrics', () => { let esClient: ReturnType<typeof elasticsearchServiceMock.createElasticsearchClient>; @@ -100,6 +100,10 @@ describe('Detections Usage and Metrics', () => { has_legacy_notification: false, has_notification: false, has_legacy_investigation_field: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + alert_suppression_fields_count: 0, }, ], detection_rule_usage: { @@ -114,6 +118,7 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, elastic_total: { alerts: 3400, @@ -125,6 +130,7 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, }, }, @@ -167,6 +173,7 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, query: { alerts: 800, @@ -178,6 +185,7 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, }, }, @@ -211,6 +219,7 @@ describe('Detections Usage and Metrics', () => { detection_rule_detail: [ { alert_count_daily: 0, + alert_suppression_fields_count: 0, cases_count_total: 1, created_on: '2021-03-23T17:15:59.634Z', elastic_rule: true, @@ -223,6 +232,9 @@ describe('Detections Usage and Metrics', () => { has_legacy_notification: false, has_notification: false, has_legacy_investigation_field: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, }, ], detection_rule_usage: { @@ -237,6 +249,7 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, query: { alerts: 0, @@ -248,6 +261,7 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, }, }, diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts index 4313abdc336bd..835f43b05e4f4 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/get_initial_usage.ts @@ -11,8 +11,23 @@ import type { RulesTypeUsage, SingleEventLogStatusMetric, SingleEventMetric, + AlertSuppressionUsage, } from './types'; +export const initialAlertSuppression: AlertSuppressionUsage = { + enabled: 0, + disabled: 0, + suppressed_per_time_period: 0, + suppressed_per_rule_execution: 0, + suppressed_fields_count: { + one: 0, + two: 0, + three: 0, + }, + suppresses_missing_fields: 0, + does_not_suppress_missing_fields: 0, +}; + /** * Default detection rule usage count, split by type + elastic/custom */ @@ -27,6 +42,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, threshold: { enabled: 0, @@ -38,6 +54,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, eql: { enabled: 0, @@ -49,6 +66,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, machine_learning: { enabled: 0, @@ -60,6 +78,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, threat_match: { enabled: 0, @@ -71,6 +90,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, new_terms: { enabled: 0, @@ -82,6 +102,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, esql: { enabled: 0, @@ -93,6 +114,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, elastic_total: { enabled: 0, @@ -104,6 +126,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, custom_total: { enabled: 0, @@ -115,6 +138,7 @@ export const getInitialRulesUsage = (): RulesTypeUsage => ({ notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: initialAlertSuppression, }, }); diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts index 116015b25db9d..7e3dc6ee18169 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/transform_utils/get_rule_object_correlations.ts @@ -8,6 +8,7 @@ import type { SavedObjectsFindResult } from '@kbn/core/server'; import type { RuleMetric } from '../types'; import type { RuleSearchResult } from '../../../types'; +import { getAlertSuppressionUsage } from '../usage_utils/get_alert_suppression_usage'; export interface RuleObjectCorrelationsOptions { ruleResults: Array<SavedObjectsFindResult<RuleSearchResult>>; @@ -41,6 +42,13 @@ export const getRuleObjectCorrelations = ({ attributes.actions.length > 0 && attributes.muteAll !== true; + const { + hasAlertSuppressionPerRuleExecution, + hasAlertSuppressionPerTimePeriod, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress, + alertSuppressionFieldsCount, + } = getAlertSuppressionUsage(attributes); + return { rule_name: attributes.name, rule_id: attributes.params.ruleId, @@ -56,6 +64,11 @@ export const getRuleObjectCorrelations = ({ has_legacy_notification: hasLegacyNotification, has_notification: hasNotification, has_legacy_investigation_field: Array.isArray(attributes.params.investigationFields), + has_alert_suppression_per_rule_execution: hasAlertSuppressionPerRuleExecution, + has_alert_suppression_per_time_period: hasAlertSuppressionPerTimePeriod, + has_alert_suppression_missing_fields_strategy_do_not_suppress: + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress, + alert_suppression_fields_count: alertSuppressionFieldsCount, }; }); }; diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts index e212ba8a9e15e..bd340d76181d3 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/types.ts @@ -5,6 +5,20 @@ * 2.0. */ +export interface AlertSuppressionUsage { + enabled: number; + disabled: number; + suppressed_fields_count: { + one: number; + two: number; + three: number; + }; + suppressed_per_time_period: number; + suppressed_per_rule_execution: number; + suppresses_missing_fields: number; + does_not_suppress_missing_fields: number; +} + export interface FeatureTypeUsage { enabled: number; disabled: number; @@ -15,6 +29,7 @@ export interface FeatureTypeUsage { notifications_enabled: number; notifications_disabled: number; legacy_investigation_fields: number; + alert_suppression: AlertSuppressionUsage; } export interface RulesTypeUsage { @@ -49,6 +64,10 @@ export interface RuleMetric { has_legacy_notification: boolean; has_notification: boolean; has_legacy_investigation_field: boolean; + has_alert_suppression_per_rule_execution: boolean; + has_alert_suppression_per_time_period: boolean; + has_alert_suppression_missing_fields_strategy_do_not_suppress: boolean; + alert_suppression_fields_count: number; } /** diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts index 3edacf0ae9e1b..b6884222ff2c7 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/update_usage.test.ts @@ -18,6 +18,10 @@ interface StubRuleOptions { hasLegacyNotification: boolean; hasNotification: boolean; hasLegacyInvestigationField: boolean; + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: boolean; + hasAlertSuppressionPerRuleExecution: boolean; + hasAlertSuppressionPerTimePeriod: boolean; + alertSuppressionFieldsCount: number; } const createStubRule = ({ @@ -29,6 +33,10 @@ const createStubRule = ({ hasLegacyNotification, hasNotification, hasLegacyInvestigationField, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress, + hasAlertSuppressionPerRuleExecution, + hasAlertSuppressionPerTimePeriod, + alertSuppressionFieldsCount, }: StubRuleOptions): RuleMetric => ({ rule_name: 'rule-name', rule_id: 'id-123', @@ -43,6 +51,11 @@ const createStubRule = ({ has_legacy_notification: hasLegacyNotification, has_notification: hasNotification, has_legacy_investigation_field: hasLegacyInvestigationField, + has_alert_suppression_missing_fields_strategy_do_not_suppress: + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress, + has_alert_suppression_per_rule_execution: hasAlertSuppressionPerRuleExecution, + has_alert_suppression_per_time_period: hasAlertSuppressionPerTimePeriod, + alert_suppression_fields_count: alertSuppressionFieldsCount, }); describe('Detections Usage and Metrics', () => { @@ -57,6 +70,10 @@ describe('Detections Usage and Metrics', () => { hasLegacyNotification: false, hasNotification: false, hasLegacyInvestigationField: false, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: false, + hasAlertSuppressionPerRuleExecution: true, + hasAlertSuppressionPerTimePeriod: false, + alertSuppressionFieldsCount: 3, }); const usage = updateRuleUsage(stubRule, getInitialRulesUsage()); @@ -72,6 +89,19 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: { + disabled: 0, + does_not_suppress_missing_fields: 0, + enabled: 1, + suppressed_fields_count: { + one: 0, + three: 1, + two: 0, + }, + suppressed_per_rule_execution: 1, + suppressed_per_time_period: 0, + suppresses_missing_fields: 1, + }, }, eql: { alerts: 1, @@ -83,6 +113,19 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: { + disabled: 0, + does_not_suppress_missing_fields: 0, + enabled: 1, + suppressed_fields_count: { + one: 0, + three: 1, + two: 0, + }, + suppressed_per_rule_execution: 1, + suppressed_per_time_period: 0, + suppresses_missing_fields: 1, + }, }, }); }); @@ -97,6 +140,10 @@ describe('Detections Usage and Metrics', () => { hasLegacyNotification: false, hasNotification: false, hasLegacyInvestigationField: false, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: true, + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: false, + alertSuppressionFieldsCount: 0, }); const stubQueryRuleOne = createStubRule({ ruleType: 'query', @@ -107,6 +154,10 @@ describe('Detections Usage and Metrics', () => { hasLegacyNotification: false, hasNotification: false, hasLegacyInvestigationField: true, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: true, + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: false, + alertSuppressionFieldsCount: 0, }); const stubQueryRuleTwo = createStubRule({ ruleType: 'query', @@ -117,6 +168,10 @@ describe('Detections Usage and Metrics', () => { hasLegacyNotification: false, hasNotification: false, hasLegacyInvestigationField: false, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: true, + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: true, + alertSuppressionFieldsCount: 2, }); const stubMachineLearningOne = createStubRule({ ruleType: 'machine_learning', @@ -127,6 +182,10 @@ describe('Detections Usage and Metrics', () => { hasLegacyNotification: false, hasNotification: false, hasLegacyInvestigationField: false, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: true, + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: true, + alertSuppressionFieldsCount: 2, }); const stubMachineLearningTwo = createStubRule({ ruleType: 'machine_learning', @@ -137,6 +196,10 @@ describe('Detections Usage and Metrics', () => { hasLegacyNotification: false, hasNotification: false, hasLegacyInvestigationField: false, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: true, + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: false, + alertSuppressionFieldsCount: 0, }); let usage = updateRuleUsage(stubEqlRule, getInitialRulesUsage()); @@ -157,6 +220,19 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: { + disabled: 1, + does_not_suppress_missing_fields: 2, + enabled: 1, + suppressed_fields_count: { + one: 0, + three: 0, + two: 2, + }, + suppressed_per_rule_execution: 0, + suppressed_per_time_period: 2, + suppresses_missing_fields: 0, + }, }, elastic_total: { alerts: 28, @@ -168,6 +244,19 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 1, + alert_suppression: { + disabled: 0, + does_not_suppress_missing_fields: 0, + enabled: 0, + suppressed_fields_count: { + one: 0, + three: 0, + two: 0, + }, + suppressed_per_rule_execution: 0, + suppressed_per_time_period: 0, + suppresses_missing_fields: 0, + }, }, eql: { alerts: 1, @@ -179,6 +268,19 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: { + disabled: 0, + does_not_suppress_missing_fields: 0, + enabled: 0, + suppressed_fields_count: { + one: 0, + three: 0, + two: 0, + }, + suppressed_per_rule_execution: 0, + suppressed_per_time_period: 0, + suppresses_missing_fields: 0, + }, }, machine_learning: { alerts: 22, @@ -190,6 +292,19 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: { + disabled: 1, + does_not_suppress_missing_fields: 1, + enabled: 0, + suppressed_fields_count: { + one: 0, + three: 0, + two: 1, + }, + suppressed_per_rule_execution: 0, + suppressed_per_time_period: 1, + suppresses_missing_fields: 0, + }, }, query: { alerts: 10, @@ -201,6 +316,19 @@ describe('Detections Usage and Metrics', () => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 1, + alert_suppression: { + disabled: 0, + does_not_suppress_missing_fields: 1, + enabled: 1, + suppressed_fields_count: { + one: 0, + three: 0, + two: 1, + }, + suppressed_per_rule_execution: 0, + suppressed_per_time_period: 1, + suppresses_missing_fields: 0, + }, }, }); }); @@ -279,6 +407,10 @@ describe('Detections Usage and Metrics', () => { alertCount: 0, caseCount: 0, hasLegacyInvestigationField, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: false, + hasAlertSuppressionPerRuleExecution: true, + hasAlertSuppressionPerTimePeriod: false, + alertSuppressionFieldsCount: 3, }); const usage = updateRuleUsage(rule1, getInitialRulesUsage()) as ReturnType< typeof updateRuleUsage @@ -303,6 +435,10 @@ describe('Detections Usage and Metrics', () => { alertCount: 0, caseCount: 0, hasLegacyInvestigationField, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: false, + hasAlertSuppressionPerRuleExecution: true, + hasAlertSuppressionPerTimePeriod: false, + alertSuppressionFieldsCount: 3, }); const usageAddedByOne = updateRuleUsage(rule2, usage) as ReturnType< typeof updateRuleUsage diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/get_alert_suppression_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/get_alert_suppression_usage.ts new file mode 100644 index 0000000000000..a4fab29e0a216 --- /dev/null +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/get_alert_suppression_usage.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 { RuleSearchResult } from '../../../types'; + +export const getAlertSuppressionUsage = ( + ruleAttributes: RuleSearchResult +): { + hasAlertSuppressionPerRuleExecution: boolean; + hasAlertSuppressionPerTimePeriod: boolean; + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: boolean; + alertSuppressionFieldsCount: number; +} => { + if (ruleAttributes.params.alertSuppression == null) { + return { + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: false, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: false, + alertSuppressionFieldsCount: 0, + }; + } + + switch (ruleAttributes.params.type) { + case 'threshold': + return { + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: true, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: false, + alertSuppressionFieldsCount: ruleAttributes.params?.threshold?.field?.length || 0, + }; + case 'query': + case 'saved_query': + case 'new_terms': + case 'threat_match': + case 'machine_learning': + case 'esql': + case 'eql': + return { + hasAlertSuppressionPerRuleExecution: + ruleAttributes.params.alertSuppression.duration == null, + hasAlertSuppressionPerTimePeriod: ruleAttributes.params.alertSuppression.duration != null, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: + ruleAttributes.params.alertSuppression.missingFieldsStrategy === 'doNotSuppress', + alertSuppressionFieldsCount: ruleAttributes.params.alertSuppression.groupBy?.length || 0, + }; + default: + return { + hasAlertSuppressionPerRuleExecution: false, + hasAlertSuppressionPerTimePeriod: false, + hasAlertSuppressionMissingFieldsStrategyDoNotSuppress: false, + alertSuppressionFieldsCount: 0, + }; + } +}; diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_alert_suppression_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_alert_suppression_usage.ts new file mode 100644 index 0000000000000..085e5f4d90ec9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_alert_suppression_usage.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertSuppressionUsage, RuleMetric, FeatureTypeUsage } from '../types'; + +export interface UpdateAlertSuppressionUsage { + detectionRuleMetric: RuleMetric; + usage: FeatureTypeUsage; +} + +export const updateAlertSuppressionUsage = ({ + detectionRuleMetric, + usage, +}: UpdateAlertSuppressionUsage): AlertSuppressionUsage => { + const isAlertSuppressionConfigured = + detectionRuleMetric.has_alert_suppression_per_rule_execution || + detectionRuleMetric.has_alert_suppression_per_time_period; + + // if rule does not have suppression configuration alert suppression usage + // returned unchanged + if (!isAlertSuppressionConfigured) { + return usage.alert_suppression; + } + + return { + enabled: detectionRuleMetric.enabled + ? usage.alert_suppression.enabled + 1 + : usage.alert_suppression.enabled, + disabled: !detectionRuleMetric.enabled + ? usage.alert_suppression.disabled + 1 + : usage.alert_suppression.disabled, + suppressed_fields_count: { + one: + detectionRuleMetric.alert_suppression_fields_count === 1 + ? usage.alert_suppression.suppressed_fields_count.one + 1 + : usage.alert_suppression.suppressed_fields_count.one, + two: + detectionRuleMetric.alert_suppression_fields_count === 2 + ? usage.alert_suppression.suppressed_fields_count.two + 1 + : usage.alert_suppression.suppressed_fields_count.two, + three: + detectionRuleMetric.alert_suppression_fields_count === 3 + ? usage.alert_suppression.suppressed_fields_count.three + 1 + : usage.alert_suppression.suppressed_fields_count.three, + }, + suppressed_per_time_period: detectionRuleMetric.has_alert_suppression_per_time_period + ? usage.alert_suppression.suppressed_per_time_period + 1 + : usage.alert_suppression.suppressed_per_time_period, + suppressed_per_rule_execution: detectionRuleMetric.has_alert_suppression_per_rule_execution + ? usage.alert_suppression.suppressed_per_rule_execution + 1 + : usage.alert_suppression.suppressed_per_rule_execution, + suppresses_missing_fields: + !detectionRuleMetric.has_alert_suppression_missing_fields_strategy_do_not_suppress + ? usage.alert_suppression.suppresses_missing_fields + 1 + : usage.alert_suppression.suppresses_missing_fields, + does_not_suppress_missing_fields: + detectionRuleMetric.has_alert_suppression_missing_fields_strategy_do_not_suppress + ? usage.alert_suppression.does_not_suppress_missing_fields + 1 + : usage.alert_suppression.does_not_suppress_missing_fields, + }; +}; diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_query_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_query_usage.ts index e6c4e897b0b9b..5245d2c9abeeb 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_query_usage.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_query_usage.ts @@ -7,6 +7,7 @@ import type { RulesTypeUsage, RuleMetric, FeatureTypeUsage } from '../types'; import { getNotificationsEnabledDisabled } from './get_notifications_enabled_disabled'; +import { updateAlertSuppressionUsage } from './update_alert_suppression_usage'; export interface UpdateQueryUsageOptions { ruleType: keyof RulesTypeUsage; @@ -47,5 +48,6 @@ export const updateQueryUsage = ({ legacy_investigation_fields: detectionRuleMetric.has_legacy_investigation_field ? usage[ruleType].legacy_investigation_fields + 1 : usage[ruleType].legacy_investigation_fields, + alert_suppression: updateAlertSuppressionUsage({ usage: usage[ruleType], detectionRuleMetric }), }; }; diff --git a/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_total_usage.ts b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_total_usage.ts index 3a63af1ce081a..c7ce17f8ab0e3 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_total_usage.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/rules/usage_utils/update_total_usage.ts @@ -7,6 +7,7 @@ import type { RulesTypeUsage, RuleMetric, FeatureTypeUsage } from '../types'; import { getNotificationsEnabledDisabled } from './get_notifications_enabled_disabled'; +import { updateAlertSuppressionUsage } from './update_alert_suppression_usage'; export interface UpdateTotalUsageOptions { detectionRuleMetric: RuleMetric; @@ -50,5 +51,9 @@ export const updateTotalUsage = ({ legacy_investigation_fields: detectionRuleMetric.has_legacy_investigation_field ? updatedUsage[totalType].legacy_investigation_fields + 1 : updatedUsage[totalType].legacy_investigation_fields, + alert_suppression: updateAlertSuppressionUsage({ + usage: updatedUsage[totalType], + detectionRuleMetric, + }), }; }; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index ba89ca2864d74..bdaf656b9c986 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -208,5 +208,8 @@ "@kbn/core-theme-browser", "@kbn/integration-assistant-plugin", "@kbn/avc-banner", + "@kbn/esql-ast", + "@kbn/esql-validation-autocomplete", + "@kbn/config", ] } diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts index b7c99e9765dac..25cb2dae38192 100644 --- a/x-pack/plugins/serverless_observability/public/plugin.ts +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -59,12 +59,13 @@ export class ServerlessObservabilityPlugin observabilityAiAssistantManagement: { category: appCategories.OTHER, title: i18n.translate('xpack.serverlessObservability.aiAssistantManagementTitle', { - defaultMessage: 'AI assistant for Observability settings', + defaultMessage: 'AI Assistant for Observability Settings', }), description: i18n.translate( 'xpack.serverlessObservability.aiAssistantManagementDescription', { - defaultMessage: 'Manage your AI assistant for Observability settings.', + defaultMessage: + 'Manage knowledge base and control assistant behavior, including response language.', } ), icon: 'sparkles', diff --git a/x-pack/plugins/task_manager/kibana.jsonc b/x-pack/plugins/task_manager/kibana.jsonc index e1141bbc58377..33edc225e42c1 100644 --- a/x-pack/plugins/task_manager/kibana.jsonc +++ b/x-pack/plugins/task_manager/kibana.jsonc @@ -11,6 +11,7 @@ "task_manager" ], "optionalPlugins": [ + "cloud", "usageCollection" ] } diff --git a/x-pack/plugins/task_manager/server/MONITORING.md b/x-pack/plugins/task_manager/server/MONITORING.md index 02946b9b3e53f..c4e66ab92bad5 100644 --- a/x-pack/plugins/task_manager/server/MONITORING.md +++ b/x-pack/plugins/task_manager/server/MONITORING.md @@ -50,7 +50,7 @@ The root `timestamp` is the time in which the summary was exposed (either to the Follow this step-by-step guide to make sense of the stats: https://www.elastic.co/guide/en/kibana/master/task-manager-troubleshooting.html#task-manager-diagnosing-root-cause #### The Configuration Section -The `configuration` section summarizes Task Manager's current configuration, including dynamic configurations which change over time, such as `poll_interval` and `max_workers` which adjust in reaction to changing load on the system. +The `configuration` section summarizes Task Manager's current configuration, including dynamic configurations which change over time, such as `poll_interval` and `capacity` which adjust in reaction to changing load on the system. These are "Hot" stats which are updated whenever a change happens in the configuration. @@ -69,8 +69,8 @@ The `runtime` tracks Task Manager's performance as it runs, making note of task These include: - The time it takes a task to run (p50, p90, p95 & p99, using a configurable running average window, `50` by default) - The average _drift_ that tasks experience (p50, p90, p95 & p99, using the same configurable running average window as above). Drift tells us how long after a task's scheduled a task typically executes. - - The average _load_ (p50, p90, p95 & p99, using the same configurable running average window as above). Load tells us what percentage of workers is in use at the end of each polling cycle. - - The polling rate (the timestamp of the last time a polling cycle completed), the polling health stats (number of version clashes and mismatches) and the result [`No tasks | Filled task pool | Unexpectedly ran out of workers`] frequency the past 50 polling cycles (using the same window size as the one used for running averages) + - The average _load_ (p50, p90, p95 & p99, using the same configurable running average window as above). Load tells us what percentage of capacity is in use at the end of each polling cycle. + - The polling rate (the timestamp of the last time a polling cycle completed), the polling health stats (number of version clashes and mismatches) and the result [`No tasks | Filled task pool | Unexpectedly ran out of capacity`] frequency the past 50 polling cycles (using the same window size as the one used for running averages) - The `Success | Retry | Failure ratio` by task type. This is different than the workload stats which tell you what's in the queue, but ca't keep track of retries and of non recurring tasks as they're wiped off the index when completed. These are "Hot" stats which are updated reactively as Tasks are executed and interacted with. diff --git a/x-pack/plugins/task_manager/server/config.test.ts b/x-pack/plugins/task_manager/server/config.test.ts index bb59a73a305d6..81e9e24ea4586 100644 --- a/x-pack/plugins/task_manager/server/config.test.ts +++ b/x-pack/plugins/task_manager/server/config.test.ts @@ -23,7 +23,6 @@ describe('config validation', () => { "warn_threshold": 5000, }, "max_attempts": 3, - "max_workers": 10, "metrics_reset_interval": 30000, "monitored_aggregated_stats_refresh_rate": 60000, "monitored_stats_health_verbose_log": Object { @@ -81,7 +80,6 @@ describe('config validation', () => { "warn_threshold": 5000, }, "max_attempts": 3, - "max_workers": 10, "metrics_reset_interval": 30000, "monitored_aggregated_stats_refresh_rate": 60000, "monitored_stats_health_verbose_log": Object { @@ -137,7 +135,6 @@ describe('config validation', () => { "warn_threshold": 5000, }, "max_attempts": 3, - "max_workers": 10, "metrics_reset_interval": 30000, "monitored_aggregated_stats_refresh_rate": 60000, "monitored_stats_health_verbose_log": Object { diff --git a/x-pack/plugins/task_manager/server/config.ts b/x-pack/plugins/task_manager/server/config.ts index eec63c5be489c..f0f4031a4c8ac 100644 --- a/x-pack/plugins/task_manager/server/config.ts +++ b/x-pack/plugins/task_manager/server/config.ts @@ -8,6 +8,9 @@ import { schema, TypeOf } from '@kbn/config-schema'; export const MAX_WORKERS_LIMIT = 100; +export const DEFAULT_CAPACITY = 10; +export const MAX_CAPACITY = 50; +export const MIN_CAPACITY = 5; export const DEFAULT_MAX_WORKERS = 10; export const DEFAULT_POLL_INTERVAL = 3000; export const DEFAULT_VERSION_CONFLICT_THRESHOLD = 80; @@ -64,6 +67,8 @@ const requestTimeoutsConfig = schema.object({ export const configSchema = schema.object( { allow_reading_invalid_state: schema.boolean({ defaultValue: true }), + /* The number of normal cost tasks that this Kibana instance will run simultaneously */ + capacity: schema.maybe(schema.number({ min: MIN_CAPACITY, max: MAX_CAPACITY })), ephemeral_tasks: schema.object({ enabled: schema.boolean({ defaultValue: false }), /* How many requests can Task Manager buffer before it rejects new requests. */ @@ -81,11 +86,12 @@ export const configSchema = schema.object( min: 1, }), /* The maximum number of tasks that this Kibana instance will run simultaneously. */ - max_workers: schema.number({ - defaultValue: DEFAULT_MAX_WORKERS, - // disable the task manager rather than trying to specify it with 0 workers - min: 1, - }), + max_workers: schema.maybe( + schema.number({ + // disable the task manager rather than trying to specify it with 0 workers + min: 1, + }) + ), /* The interval at which monotonically increasing metrics counters will reset */ metrics_reset_interval: schema.number({ defaultValue: DEFAULT_METRICS_RESET_INTERVAL, diff --git a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts index 19cfa2943502c..2a6f1bf8c33b8 100644 --- a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.test.ts @@ -18,7 +18,7 @@ import { v4 as uuidv4 } from 'uuid'; import { asTaskPollingCycleEvent, asTaskRunEvent, TaskPersistence } from './task_events'; import { TaskRunResult } from './task_running'; import { TaskPoolRunResult } from './task_pool'; -import { TaskPoolMock } from './task_pool.mock'; +import { TaskPoolMock } from './task_pool/task_pool.mock'; import { executionContextServiceMock } from '@kbn/core/server/mocks'; import { taskManagerMock } from './mocks'; @@ -45,7 +45,6 @@ describe('EphemeralTaskLifecycle', () => { definitions: new TaskTypeDictionary(taskManagerLogger), executionContext, config: { - max_workers: 10, max_attempts: 9, poll_interval: 6000000, version_conflict_threshold: 80, @@ -156,7 +155,7 @@ describe('EphemeralTaskLifecycle', () => { expect(ephemeralTaskLifecycle.attemptToRun(task)).toMatchObject(asOk(task)); poolCapacity.mockReturnValue({ - availableWorkers: 10, + availableCapacity: 10, }); lifecycleEvent$.next( @@ -179,7 +178,7 @@ describe('EphemeralTaskLifecycle', () => { expect(ephemeralTaskLifecycle.attemptToRun(task)).toMatchObject(asOk(task)); poolCapacity.mockReturnValue({ - availableWorkers: 10, + availableCapacity: 10, }); lifecycleEvent$.next( @@ -216,7 +215,7 @@ describe('EphemeralTaskLifecycle', () => { expect(ephemeralTaskLifecycle.attemptToRun(tasks[2])).toMatchObject(asOk(tasks[2])); poolCapacity.mockReturnValue({ - availableWorkers: 2, + availableCapacity: 2, }); lifecycleEvent$.next( @@ -256,9 +255,9 @@ describe('EphemeralTaskLifecycle', () => { // pool has capacity for both poolCapacity.mockReturnValue({ - availableWorkers: 10, + availableCapacity: 10, }); - pool.getOccupiedWorkersByType.mockReturnValue(0); + pool.getUsedCapacityByType.mockReturnValue(0); lifecycleEvent$.next( asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed })) @@ -296,10 +295,10 @@ describe('EphemeralTaskLifecycle', () => { // pool has capacity in general poolCapacity.mockReturnValue({ - availableWorkers: 2, + availableCapacity: 2, }); // but when we ask how many it has occupied by type - wee always have one worker already occupied by that type - pool.getOccupiedWorkersByType.mockReturnValue(1); + pool.getUsedCapacityByType.mockReturnValue(1); lifecycleEvent$.next( asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed })) @@ -308,7 +307,7 @@ describe('EphemeralTaskLifecycle', () => { expect(pool.run).toHaveBeenCalledTimes(0); // now we release the worker in the pool and cause another cycle in the epheemral queue - pool.getOccupiedWorkersByType.mockReturnValue(0); + pool.getUsedCapacityByType.mockReturnValue(0); lifecycleEvent$.next( asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed })) ); @@ -356,9 +355,9 @@ describe('EphemeralTaskLifecycle', () => { // pool has capacity for all poolCapacity.mockReturnValue({ - availableWorkers: 10, + availableCapacity: 10, }); - pool.getOccupiedWorkersByType.mockReturnValue(0); + pool.getUsedCapacityByType.mockReturnValue(0); lifecycleEvent$.next(asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed }))); @@ -389,19 +388,19 @@ describe('EphemeralTaskLifecycle', () => { expect(ephemeralTaskLifecycle.queuedTasks).toBe(3); poolCapacity.mockReturnValue({ - availableWorkers: 1, + availableCapacity: 1, }); lifecycleEvent$.next(asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed }))); expect(ephemeralTaskLifecycle.queuedTasks).toBe(2); poolCapacity.mockReturnValue({ - availableWorkers: 1, + availableCapacity: 1, }); lifecycleEvent$.next(asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed }))); expect(ephemeralTaskLifecycle.queuedTasks).toBe(1); poolCapacity.mockReturnValue({ - availableWorkers: 1, + availableCapacity: 1, }); lifecycleEvent$.next(asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed }))); expect(ephemeralTaskLifecycle.queuedTasks).toBe(0); diff --git a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts index 37cc166ece211..c7ee267b848e5 100644 --- a/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/ephemeral_task_lifecycle.ts @@ -143,13 +143,13 @@ export class EphemeralTaskLifecycle { taskType && this.definitions.get(taskType)?.maxConcurrency ? Math.max( Math.min( - this.pool.availableWorkers, + this.pool.availableCapacity(), this.definitions.get(taskType)!.maxConcurrency! - - this.pool.getOccupiedWorkersByType(taskType) + this.pool.getUsedCapacityByType(taskType) ), 0 ) - : this.pool.availableWorkers; + : this.pool.availableCapacity(); private emitEvent = (event: TaskLifecycleEvent) => { this.events$.next(event); diff --git a/x-pack/plugins/task_manager/server/index.ts b/x-pack/plugins/task_manager/server/index.ts index 8d50c37adda0b..965df090911fd 100644 --- a/x-pack/plugins/task_manager/server/index.ts +++ b/x-pack/plugins/task_manager/server/index.ts @@ -55,9 +55,6 @@ export type { export const config: PluginConfigDescriptor<TaskManagerConfig> = { schema: configSchema, - exposeToUsage: { - max_workers: true, - }, deprecations: ({ deprecate }) => { return [ deprecate('ephemeral_tasks.enabled', 'a future version', { @@ -68,6 +65,10 @@ export const config: PluginConfigDescriptor<TaskManagerConfig> = { level: 'warning', message: `Configuring "xpack.task_manager.ephemeral_tasks.request_capacity" is deprecated and will be removed in a future version. Remove this setting to increase task execution resiliency.`, }), + deprecate('max_workers', 'a future version', { + level: 'warning', + message: `Configuring "xpack.task_manager.max_workers" is deprecated and will be removed in a future version. Remove this setting and use "xpack.task_manager.capacity" instead.`, + }), (settings, fromPath, addDeprecation) => { const taskManager = get(settings, fromPath); if (taskManager?.index) { diff --git a/x-pack/plugins/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap b/x-pack/plugins/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap new file mode 100644 index 0000000000000..e59912ed91905 --- /dev/null +++ b/x-pack/plugins/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Task cost checks detects tasks with cost definitions 1`] = ` +Array [ + Object { + "cost": 10, + "taskType": "alerting:siem.indicatorRule", + }, +] +`; diff --git a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts index c0939b5b31667..cc16b8d0544cf 100644 --- a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts +++ b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts @@ -35,164 +35,362 @@ describe('managed configuration', () => { }, }; - beforeEach(async () => { - jest.resetAllMocks(); - clock = sinon.useFakeTimers(); - - const context = coreMock.createPluginInitializerContext<TaskManagerConfig>({ - max_workers: 10, - max_attempts: 9, - poll_interval: 3000, - allow_reading_invalid_state: false, - version_conflict_threshold: 80, - monitored_aggregated_stats_refresh_rate: 60000, - monitored_stats_health_verbose_log: { - enabled: false, - level: 'debug' as const, - warn_delayed_task_start_in_seconds: 60, - }, - monitored_stats_required_freshness: 4000, - monitored_stats_running_average_window: 50, - request_capacity: 1000, - monitored_task_execution_thresholds: { - default: { - error_threshold: 90, - warn_threshold: 80, - }, - custom: {}, - }, - ephemeral_tasks: { - enabled: true, - request_capacity: 10, - }, - unsafe: { - exclude_task_types: [], - authenticate_background_task_utilization: true, - }, - event_loop_delay: { - monitor: true, - warn_threshold: 5000, - }, - worker_utilization_running_average_window: 5, - metrics_reset_interval: 3000, - claim_strategy: 'default', - request_timeouts: { - update_by_query: 1000, - }, + afterEach(() => clock.restore()); + + describe('managed poll interval', () => { + beforeEach(async () => { + jest.resetAllMocks(); + clock = sinon.useFakeTimers(); + + const context = coreMock.createPluginInitializerContext<TaskManagerConfig>({ + capacity: 10, + max_attempts: 9, + poll_interval: 3000, + allow_reading_invalid_state: false, + version_conflict_threshold: 80, + monitored_aggregated_stats_refresh_rate: 60000, + monitored_stats_health_verbose_log: { + enabled: false, + level: 'debug' as const, + warn_delayed_task_start_in_seconds: 60, + }, + monitored_stats_required_freshness: 4000, + monitored_stats_running_average_window: 50, + request_capacity: 1000, + monitored_task_execution_thresholds: { + default: { + error_threshold: 90, + warn_threshold: 80, + }, + custom: {}, + }, + ephemeral_tasks: { + enabled: true, + request_capacity: 10, + }, + unsafe: { + exclude_task_types: [], + authenticate_background_task_utilization: true, + }, + event_loop_delay: { + monitor: true, + warn_threshold: 5000, + }, + worker_utilization_running_average_window: 5, + metrics_reset_interval: 3000, + claim_strategy: 'default', + request_timeouts: { + update_by_query: 1000, + }, + }); + logger = context.logger.get('taskManager'); + + const taskManager = new TaskManagerPlugin(context); + ( + await taskManager.setup(coreMock.createSetup(), { usageCollection: undefined }) + ).registerTaskDefinitions({ + foo: { + title: 'Foo', + createTaskRunner: jest.fn(), + }, + }); + + const coreStart = coreMock.createStart(); + coreStart.elasticsearch = esStart; + esStart.client.asInternalUser.child.mockReturnValue( + esStart.client.asInternalUser as unknown as Client + ); + coreStart.savedObjects.createInternalRepository.mockReturnValue(savedObjectsClient); + taskManagerStart = await taskManager.start(coreStart, {}); + + // force rxjs timers to fire when they are scheduled for setTimeout(0) as the + // sinon fake timers cause them to stall + clock.tick(0); }); - logger = context.logger.get('taskManager'); - - const taskManager = new TaskManagerPlugin(context); - ( - await taskManager.setup(coreMock.createSetup(), { usageCollection: undefined }) - ).registerTaskDefinitions({ - foo: { - title: 'Foo', - createTaskRunner: jest.fn(), - }, + + test('should increase poll interval when Elasticsearch returns 429 error', async () => { + savedObjectsClient.create.mockRejectedValueOnce( + SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') + ); + + // Cause "too many requests" error to be thrown + await expect( + taskManagerStart.schedule({ + taskType: 'foo', + state: {}, + params: {}, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3600ms'); }); - const coreStart = coreMock.createStart(); - coreStart.elasticsearch = esStart; - esStart.client.asInternalUser.child.mockReturnValue( - esStart.client.asInternalUser as unknown as Client - ); - coreStart.savedObjects.createInternalRepository.mockReturnValue(savedObjectsClient); - taskManagerStart = await taskManager.start(coreStart); - - // force rxjs timers to fire when they are scheduled for setTimeout(0) as the - // sinon fake timers cause them to stall - clock.tick(0); - }); + test('should increase poll interval when Elasticsearch returns "cannot execute [inline] scripts" error', async () => { + const childEsClient = esStart.client.asInternalUser.child({}) as jest.Mocked<Client>; + childEsClient.search.mockImplementationOnce(async () => { + throw inlineScriptError; + }); - afterEach(() => clock.restore()); + await expect(taskManagerStart.fetch({})).rejects.toThrowErrorMatchingInlineSnapshot( + `"cannot execute [inline] scripts\\" error"` + ); - test('should lower max workers when Elasticsearch returns 429 error', async () => { - savedObjectsClient.create.mockRejectedValueOnce( - SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') - ); - - // Cause "too many requests" error to be thrown - await expect( - taskManagerStart.schedule({ - taskType: 'foo', - state: {}, - params: {}, - }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - - expect(logger.warn).toHaveBeenCalledWith( - 'Max workers configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Max workers configuration changing from 10 to 8 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' - ); - expect(logger.debug).toHaveBeenCalledWith('Task pool now using 10 as the max worker value'); - }); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); - test('should increase poll interval when Elasticsearch returns 429 error', async () => { - savedObjectsClient.create.mockRejectedValueOnce( - SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') - ); - - // Cause "too many requests" error to be thrown - await expect( - taskManagerStart.schedule({ - taskType: 'foo', - state: {}, - params: {}, - }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - - expect(logger.warn).toHaveBeenCalledWith( - 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' - ); - expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3600ms'); + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3600ms'); + }); }); - test('should lower max workers when Elasticsearch returns "cannot execute [inline] scripts" error', async () => { - const childEsClient = esStart.client.asInternalUser.child({}) as jest.Mocked<Client>; - childEsClient.search.mockImplementationOnce(async () => { - throw inlineScriptError; + describe('managed capacity with default claim strategy', () => { + beforeEach(async () => { + jest.resetAllMocks(); + clock = sinon.useFakeTimers(); + + const context = coreMock.createPluginInitializerContext<TaskManagerConfig>({ + capacity: 10, + max_attempts: 9, + poll_interval: 3000, + allow_reading_invalid_state: false, + version_conflict_threshold: 80, + monitored_aggregated_stats_refresh_rate: 60000, + monitored_stats_health_verbose_log: { + enabled: false, + level: 'debug' as const, + warn_delayed_task_start_in_seconds: 60, + }, + monitored_stats_required_freshness: 4000, + monitored_stats_running_average_window: 50, + request_capacity: 1000, + monitored_task_execution_thresholds: { + default: { + error_threshold: 90, + warn_threshold: 80, + }, + custom: {}, + }, + ephemeral_tasks: { + enabled: true, + request_capacity: 10, + }, + unsafe: { + exclude_task_types: [], + authenticate_background_task_utilization: true, + }, + event_loop_delay: { + monitor: true, + warn_threshold: 5000, + }, + worker_utilization_running_average_window: 5, + metrics_reset_interval: 3000, + claim_strategy: 'default', + request_timeouts: { + update_by_query: 1000, + }, + }); + logger = context.logger.get('taskManager'); + + const taskManager = new TaskManagerPlugin(context); + ( + await taskManager.setup(coreMock.createSetup(), { usageCollection: undefined }) + ).registerTaskDefinitions({ + foo: { + title: 'Foo', + createTaskRunner: jest.fn(), + }, + }); + + const coreStart = coreMock.createStart(); + coreStart.elasticsearch = esStart; + esStart.client.asInternalUser.child.mockReturnValue( + esStart.client.asInternalUser as unknown as Client + ); + coreStart.savedObjects.createInternalRepository.mockReturnValue(savedObjectsClient); + taskManagerStart = await taskManager.start(coreStart, {}); + + // force rxjs timers to fire when they are scheduled for setTimeout(0) as the + // sinon fake timers cause them to stall + clock.tick(0); }); - await expect(taskManagerStart.fetch({})).rejects.toThrowErrorMatchingInlineSnapshot( - `"cannot execute [inline] scripts\\" error"` - ); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - - expect(logger.warn).toHaveBeenCalledWith( - 'Max workers configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Max workers configuration changing from 10 to 8 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' - ); - expect(logger.debug).toHaveBeenCalledWith('Task pool now using 10 as the max worker value'); + test('should lower capacity when Elasticsearch returns 429 error', async () => { + savedObjectsClient.create.mockRejectedValueOnce( + SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') + ); + + // Cause "too many requests" error to be thrown + await expect( + taskManagerStart.schedule({ + taskType: 'foo', + state: {}, + params: {}, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.warn).toHaveBeenCalledWith( + 'Capacity configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Capacity configuration changing from 10 to 8 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Task pool now using 10 as the max worker value which is based on a capacity of 10' + ); + }); + + test('should lower capacity when Elasticsearch returns "cannot execute [inline] scripts" error', async () => { + const childEsClient = esStart.client.asInternalUser.child({}) as jest.Mocked<Client>; + childEsClient.search.mockImplementationOnce(async () => { + throw inlineScriptError; + }); + + await expect(taskManagerStart.fetch({})).rejects.toThrowErrorMatchingInlineSnapshot( + `"cannot execute [inline] scripts\\" error"` + ); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.warn).toHaveBeenCalledWith( + 'Capacity configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Capacity configuration changing from 10 to 8 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Task pool now using 10 as the max worker value which is based on a capacity of 10' + ); + }); }); - test('should increase poll interval when Elasticsearch returns "cannot execute [inline] scripts" error', async () => { - const childEsClient = esStart.client.asInternalUser.child({}) as jest.Mocked<Client>; - childEsClient.search.mockImplementationOnce(async () => { - throw inlineScriptError; + describe('managed capacity with mget claim strategy', () => { + beforeEach(async () => { + jest.resetAllMocks(); + clock = sinon.useFakeTimers(); + + const context = coreMock.createPluginInitializerContext<TaskManagerConfig>({ + capacity: 10, + max_attempts: 9, + poll_interval: 3000, + allow_reading_invalid_state: false, + version_conflict_threshold: 80, + monitored_aggregated_stats_refresh_rate: 60000, + monitored_stats_health_verbose_log: { + enabled: false, + level: 'debug' as const, + warn_delayed_task_start_in_seconds: 60, + }, + monitored_stats_required_freshness: 4000, + monitored_stats_running_average_window: 50, + request_capacity: 1000, + monitored_task_execution_thresholds: { + default: { + error_threshold: 90, + warn_threshold: 80, + }, + custom: {}, + }, + ephemeral_tasks: { + enabled: true, + request_capacity: 10, + }, + unsafe: { + exclude_task_types: [], + authenticate_background_task_utilization: true, + }, + event_loop_delay: { + monitor: true, + warn_threshold: 5000, + }, + worker_utilization_running_average_window: 5, + metrics_reset_interval: 3000, + claim_strategy: 'unsafe_mget', + request_timeouts: { + update_by_query: 1000, + }, + }); + logger = context.logger.get('taskManager'); + + const taskManager = new TaskManagerPlugin(context); + ( + await taskManager.setup(coreMock.createSetup(), { usageCollection: undefined }) + ).registerTaskDefinitions({ + foo: { + title: 'Foo', + createTaskRunner: jest.fn(), + }, + }); + + const coreStart = coreMock.createStart(); + coreStart.elasticsearch = esStart; + esStart.client.asInternalUser.child.mockReturnValue( + esStart.client.asInternalUser as unknown as Client + ); + coreStart.savedObjects.createInternalRepository.mockReturnValue(savedObjectsClient); + taskManagerStart = await taskManager.start(coreStart, {}); + + // force rxjs timers to fire when they are scheduled for setTimeout(0) as the + // sinon fake timers cause them to stall + clock.tick(0); }); - await expect(taskManagerStart.fetch({})).rejects.toThrowErrorMatchingInlineSnapshot( - `"cannot execute [inline] scripts\\" error"` - ); + test('should lower capacity when Elasticsearch returns 429 error', async () => { + savedObjectsClient.create.mockRejectedValueOnce( + SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') + ); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); + // Cause "too many requests" error to be thrown + await expect( + taskManagerStart.schedule({ + taskType: 'foo', + state: {}, + params: {}, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.warn).toHaveBeenCalledWith( + 'Capacity configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Capacity configuration changing from 10 to 8 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Task pool now using 20 as the max allowed cost which is based on a capacity of 10' + ); + }); - expect(logger.warn).toHaveBeenCalledWith( - 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' - ); - expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3600ms'); + test('should lower capacity when Elasticsearch returns "cannot execute [inline] scripts" error', async () => { + const childEsClient = esStart.client.asInternalUser.child({}) as jest.Mocked<Client>; + childEsClient.search.mockImplementationOnce(async () => { + throw inlineScriptError; + }); + + await expect(taskManagerStart.fetch({})).rejects.toThrowErrorMatchingInlineSnapshot( + `"cannot execute [inline] scripts\\" error"` + ); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.warn).toHaveBeenCalledWith( + 'Capacity configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Capacity configuration changing from 10 to 8 after seeing 1 "too many request" and/or "execute [inline] script" error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Task pool now using 20 as the max allowed cost which is based on a capacity of 10' + ); + }); }); }); diff --git a/x-pack/plugins/task_manager/server/integration_tests/task_cost_check.test.ts b/x-pack/plugins/task_manager/server/integration_tests/task_cost_check.test.ts new file mode 100644 index 0000000000000..96678f714ac69 --- /dev/null +++ b/x-pack/plugins/task_manager/server/integration_tests/task_cost_check.test.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + type TestElasticsearchUtils, + type TestKibanaUtils, +} from '@kbn/core-test-helpers-kbn-server'; +import { TaskCost, TaskDefinition } from '../task'; +import { setupTestServers } from './lib'; +import { TaskTypeDictionary } from '../task_type_dictionary'; + +jest.mock('../task_type_dictionary', () => { + const actual = jest.requireActual('../task_type_dictionary'); + return { + ...actual, + TaskTypeDictionary: jest.fn().mockImplementation((opts) => { + return new actual.TaskTypeDictionary(opts); + }), + }; +}); + +// Notify response-ops if a task sets a cost to something other than `Normal` +describe('Task cost checks', () => { + let esServer: TestElasticsearchUtils; + let kibanaServer: TestKibanaUtils; + let taskTypeDictionary: TaskTypeDictionary; + + beforeAll(async () => { + const setupResult = await setupTestServers(); + esServer = setupResult.esServer; + kibanaServer = setupResult.kibanaServer; + + const mockedTaskTypeDictionary = jest.requireMock('../task_type_dictionary'); + expect(mockedTaskTypeDictionary.TaskTypeDictionary).toHaveBeenCalledTimes(1); + taskTypeDictionary = mockedTaskTypeDictionary.TaskTypeDictionary.mock.results[0].value; + }); + + afterAll(async () => { + if (kibanaServer) { + await kibanaServer.stop(); + } + if (esServer) { + await esServer.stop(); + } + }); + + it('detects tasks with cost definitions', async () => { + const taskTypes = taskTypeDictionary.getAllDefinitions(); + const taskTypesWithCost = taskTypes + .map((taskType: TaskDefinition) => + !!taskType.cost ? { taskType: taskType.type, cost: taskType.cost } : null + ) + .filter( + (tt: { taskType: string; cost: TaskCost } | null) => + null != tt && tt.cost !== TaskCost.Normal + ); + expect(taskTypesWithCost).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts b/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts index fc2f34701e3c1..49c68459982ba 100644 --- a/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts +++ b/x-pack/plugins/task_manager/server/lib/calculate_health_status.test.ts @@ -16,7 +16,6 @@ Date.now = jest.fn().mockReturnValue(new Date(now)); const logger = loggingSystemMock.create().get(); const config = { enabled: true, - max_workers: 10, index: 'foo', max_attempts: 9, poll_interval: 3000, @@ -73,6 +72,8 @@ const getStatsWithTimestamp = ({ configuration: { timestamp, value: { + capacity: { config: 10, as_cost: 20, as_workers: 10 }, + claim_strategy: 'default', request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, monitored_stats_running_average_window: 50, @@ -84,7 +85,6 @@ const getStatsWithTimestamp = ({ }, }, poll_interval: 3000, - max_workers: 10, }, status: HealthStatus.OK, }, @@ -213,24 +213,29 @@ const getStatsWithTimestamp = ({ timestamp, value: { count: 2, + cost: 4, task_types: { taskType1: { count: 1, + cost: 2, status: { idle: 1, }, }, taskType2: { count: 1, + cost: 2, status: { idle: 1, }, }, }, non_recurring: 2, + non_recurring_cost: 4, owner_ids: 0, schedule: [['5m', 2]], overdue: 0, + overdue_cost: 0, overdue_non_recurring: 0, estimated_schedule_density: [ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, diff --git a/x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts index b1d6ce92c323a..a93da63ae693a 100644 --- a/x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts +++ b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts @@ -13,6 +13,7 @@ import { ADJUST_THROUGHPUT_INTERVAL, } from './create_managed_configuration'; import { mockLogger } from '../test_utils'; +import { CLAIM_STRATEGY_DEFAULT, CLAIM_STRATEGY_MGET, TaskManagerConfig } from '../config'; describe('createManagedConfiguration()', () => { let clock: sinon.SinonFakeTimers; @@ -26,51 +27,141 @@ describe('createManagedConfiguration()', () => { afterEach(() => clock.restore()); test('returns observables with initialized values', async () => { - const maxWorkersSubscription = jest.fn(); + const capacitySubscription = jest.fn(); const pollIntervalSubscription = jest.fn(); - const { maxWorkersConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ logger, errors$: new Subject<Error>(), - startingMaxWorkers: 1, - startingPollInterval: 2, + config: { + capacity: 20, + poll_interval: 2, + } as TaskManagerConfig, }); - maxWorkersConfiguration$.subscribe(maxWorkersSubscription); + capacityConfiguration$.subscribe(capacitySubscription); pollIntervalConfiguration$.subscribe(pollIntervalSubscription); - expect(maxWorkersSubscription).toHaveBeenCalledTimes(1); - expect(maxWorkersSubscription).toHaveBeenNthCalledWith(1, 1); + expect(capacitySubscription).toHaveBeenCalledTimes(1); + expect(capacitySubscription).toHaveBeenNthCalledWith(1, 20); expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); }); + test('uses max_workers config as capacity if only max workers is defined', async () => { + const capacitySubscription = jest.fn(); + const pollIntervalSubscription = jest.fn(); + const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + logger, + errors$: new Subject<Error>(), + config: { + max_workers: 10, + poll_interval: 2, + } as TaskManagerConfig, + }); + capacityConfiguration$.subscribe(capacitySubscription); + pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + expect(capacitySubscription).toHaveBeenCalledTimes(1); + expect(capacitySubscription).toHaveBeenNthCalledWith(1, 10); + expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + }); + + test('uses max_workers config as capacity but does not exceed MAX_CAPACITY', async () => { + const capacitySubscription = jest.fn(); + const pollIntervalSubscription = jest.fn(); + const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + logger, + errors$: new Subject<Error>(), + config: { + max_workers: 1000, + poll_interval: 2, + } as TaskManagerConfig, + }); + capacityConfiguration$.subscribe(capacitySubscription); + pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + expect(capacitySubscription).toHaveBeenCalledTimes(1); + expect(capacitySubscription).toHaveBeenNthCalledWith(1, 50); + expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + }); + + test('uses provided defaultCapacity if neither capacity nor max_workers is defined', async () => { + const capacitySubscription = jest.fn(); + const pollIntervalSubscription = jest.fn(); + const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + defaultCapacity: 500, + logger, + errors$: new Subject<Error>(), + config: { + poll_interval: 2, + } as TaskManagerConfig, + }); + capacityConfiguration$.subscribe(capacitySubscription); + pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + expect(capacitySubscription).toHaveBeenCalledTimes(1); + expect(capacitySubscription).toHaveBeenNthCalledWith(1, 500); + expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + }); + + test('logs warning and uses capacity config if both capacity and max_workers is defined', async () => { + const capacitySubscription = jest.fn(); + const pollIntervalSubscription = jest.fn(); + const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + logger, + errors$: new Subject<Error>(), + config: { + capacity: 30, + max_workers: 10, + poll_interval: 2, + } as TaskManagerConfig, + }); + capacityConfiguration$.subscribe(capacitySubscription); + pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + expect(capacitySubscription).toHaveBeenCalledTimes(1); + expect(capacitySubscription).toHaveBeenNthCalledWith(1, 30); + expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + expect(logger.warn).toHaveBeenCalledWith( + `Both \"xpack.task_manager.capacity\" and \"xpack.task_manager.max_workers\" configs are set, max_workers will be ignored in favor of capacity and the setting should be removed.` + ); + }); + test(`skips errors that aren't about too many requests`, async () => { - const maxWorkersSubscription = jest.fn(); + const capacitySubscription = jest.fn(); const pollIntervalSubscription = jest.fn(); const errors$ = new Subject<Error>(); - const { maxWorkersConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ errors$, logger, - startingMaxWorkers: 100, - startingPollInterval: 100, + config: { + capacity: 10, + poll_interval: 100, + } as TaskManagerConfig, }); - maxWorkersConfiguration$.subscribe(maxWorkersSubscription); + capacityConfiguration$.subscribe(capacitySubscription); pollIntervalConfiguration$.subscribe(pollIntervalSubscription); errors$.next(new Error('foo')); clock.tick(ADJUST_THROUGHPUT_INTERVAL); - expect(maxWorkersSubscription).toHaveBeenCalledTimes(1); + expect(capacitySubscription).toHaveBeenCalledTimes(1); expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); }); - describe('maxWorker configuration', () => { - function setupScenario(startingMaxWorkers: number) { + describe('capacity configuration', () => { + function setupScenario( + startingCapacity: number, + claimStrategy: string = CLAIM_STRATEGY_DEFAULT + ) { const errors$ = new Subject<Error>(); const subscription = jest.fn(); - const { maxWorkersConfiguration$ } = createManagedConfiguration({ + const { capacityConfiguration$ } = createManagedConfiguration({ errors$, - startingMaxWorkers, logger, - startingPollInterval: 1, + config: { + capacity: startingCapacity, + poll_interval: 1, + claim_strategy: claimStrategy, + } as TaskManagerConfig, }); - maxWorkersConfiguration$.subscribe(subscription); + capacityConfiguration$.subscribe(subscription); return { subscription, errors$ }; } @@ -81,66 +172,103 @@ describe('createManagedConfiguration()', () => { afterEach(() => clock.restore()); - test('should decrease configuration at the next interval when an error is emitted', async () => { - const { subscription, errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); - expect(subscription).toHaveBeenCalledTimes(1); - clock.tick(1); - expect(subscription).toHaveBeenCalledTimes(2); - expect(subscription).toHaveBeenNthCalledWith(2, 80); - }); + describe('default claim strategy', () => { + test('should decrease configuration at the next interval when an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(10); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + expect(subscription).toHaveBeenNthCalledWith(1, 10); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 8); + }); - test('should log a warning when the configuration changes from the starting value', async () => { - const { errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - expect(logger.warn).toHaveBeenCalledWith( - 'Max workers configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' - ); - }); + test('should log a warning when the configuration changes from the starting value', async () => { + const { errors$ } = setupScenario(10); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.warn).toHaveBeenCalledWith( + 'Capacity configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + }); - test('should increase configuration back to normal incrementally after an error is emitted', async () => { - const { subscription, errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); - expect(subscription).toHaveBeenNthCalledWith(2, 80); - expect(subscription).toHaveBeenNthCalledWith(3, 84); - // 88.2- > 89 from Math.ceil - expect(subscription).toHaveBeenNthCalledWith(4, 89); - expect(subscription).toHaveBeenNthCalledWith(5, 94); - expect(subscription).toHaveBeenNthCalledWith(6, 99); - // 103.95 -> 100 from Math.min with starting value - expect(subscription).toHaveBeenNthCalledWith(7, 100); - // No new calls due to value not changing and usage of distinctUntilChanged() - expect(subscription).toHaveBeenCalledTimes(7); + test('should increase configuration back to normal incrementally after an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(10); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); + expect(subscription).toHaveBeenNthCalledWith(1, 10); + expect(subscription).toHaveBeenNthCalledWith(2, 8); + expect(subscription).toHaveBeenNthCalledWith(3, 9); + expect(subscription).toHaveBeenNthCalledWith(4, 10); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(4); + }); + + test('should keep reducing configuration when errors keep emitting until it reaches minimum', async () => { + const { subscription, errors$ } = setupScenario(10); + for (let i = 0; i < 20; i++) { + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(1, 10); + expect(subscription).toHaveBeenNthCalledWith(2, 8); + expect(subscription).toHaveBeenNthCalledWith(3, 6); + expect(subscription).toHaveBeenNthCalledWith(4, 4); + expect(subscription).toHaveBeenNthCalledWith(5, 3); + expect(subscription).toHaveBeenNthCalledWith(6, 2); + expect(subscription).toHaveBeenNthCalledWith(7, 1); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(7); + }); }); - test('should keep reducing configuration when errors keep emitting', async () => { - const { subscription, errors$ } = setupScenario(100); - for (let i = 0; i < 20; i++) { + describe('mget claim strategy', () => { + test('should decrease configuration at the next interval when an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(10, CLAIM_STRATEGY_MGET); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + expect(subscription).toHaveBeenNthCalledWith(1, 10); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 8); + }); + + test('should log a warning when the configuration changes from the starting value', async () => { + const { errors$ } = setupScenario(10, CLAIM_STRATEGY_MGET); errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); clock.tick(ADJUST_THROUGHPUT_INTERVAL); - } - expect(subscription).toHaveBeenNthCalledWith(2, 80); - expect(subscription).toHaveBeenNthCalledWith(3, 64); - // 51.2 -> 51 from Math.floor - expect(subscription).toHaveBeenNthCalledWith(4, 51); - expect(subscription).toHaveBeenNthCalledWith(5, 40); - expect(subscription).toHaveBeenNthCalledWith(6, 32); - expect(subscription).toHaveBeenNthCalledWith(7, 25); - expect(subscription).toHaveBeenNthCalledWith(8, 20); - expect(subscription).toHaveBeenNthCalledWith(9, 16); - expect(subscription).toHaveBeenNthCalledWith(10, 12); - expect(subscription).toHaveBeenNthCalledWith(11, 9); - expect(subscription).toHaveBeenNthCalledWith(12, 7); - expect(subscription).toHaveBeenNthCalledWith(13, 5); - expect(subscription).toHaveBeenNthCalledWith(14, 4); - expect(subscription).toHaveBeenNthCalledWith(15, 3); - expect(subscription).toHaveBeenNthCalledWith(16, 2); - expect(subscription).toHaveBeenNthCalledWith(17, 1); - // No new calls due to value not changing and usage of distinctUntilChanged() - expect(subscription).toHaveBeenCalledTimes(17); + expect(logger.warn).toHaveBeenCalledWith( + 'Capacity configuration is temporarily reduced after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" error(s).' + ); + }); + + test('should increase configuration back to normal incrementally after an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(10, CLAIM_STRATEGY_MGET); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); + expect(subscription).toHaveBeenNthCalledWith(1, 10); + expect(subscription).toHaveBeenNthCalledWith(2, 8); + expect(subscription).toHaveBeenNthCalledWith(3, 9); + expect(subscription).toHaveBeenNthCalledWith(4, 10); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(4); + }); + + test('should keep reducing configuration when errors keep emitting until it reaches minimum', async () => { + const { subscription, errors$ } = setupScenario(10, CLAIM_STRATEGY_MGET); + for (let i = 0; i < 20; i++) { + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(1, 10); + expect(subscription).toHaveBeenNthCalledWith(2, 8); + expect(subscription).toHaveBeenNthCalledWith(3, 6); + expect(subscription).toHaveBeenNthCalledWith(4, 5); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(4); + }); }); }); @@ -151,8 +279,10 @@ describe('createManagedConfiguration()', () => { const { pollIntervalConfiguration$ } = createManagedConfiguration({ logger, errors$, - startingPollInterval, - startingMaxWorkers: 1, + config: { + poll_interval: startingPollInterval, + capacity: 20, + } as TaskManagerConfig, }); pollIntervalConfiguration$.subscribe(subscription); return { subscription, errors$ }; diff --git a/x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts index 5c7b1a16a4308..3036eb2008de6 100644 --- a/x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts +++ b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts @@ -10,17 +10,26 @@ import { filter, mergeScan, map, scan, distinctUntilChanged, startWith } from 'r import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { Logger } from '@kbn/core/server'; import { isEsCannotExecuteScriptError } from './identify_es_error'; +import { CLAIM_STRATEGY_MGET, DEFAULT_CAPACITY, MAX_CAPACITY, TaskManagerConfig } from '../config'; +import { TaskCost } from '../task'; const FLUSH_MARKER = Symbol('flush'); export const ADJUST_THROUGHPUT_INTERVAL = 10 * 1000; export const PREFERRED_MAX_POLL_INTERVAL = 60 * 1000; + +// Capacity is measured in number of normal cost tasks that can be run +// At a minimum, we need to be able to run a single task with the greatest cost +// so we should convert the greatest cost to normal cost +export const MIN_COST = TaskCost.ExtraLarge / TaskCost.Normal; + +// For default claim strategy export const MIN_WORKERS = 1; -// When errors occur, reduce maxWorkers by MAX_WORKERS_DECREASE_PERCENTAGE -// When errors no longer occur, start increasing maxWorkers by MAX_WORKERS_INCREASE_PERCENTAGE +// When errors occur, reduce capacity by CAPACITY_DECREASE_PERCENTAGE +// When errors no longer occur, start increasing capacity by CAPACITY_INCREASE_PERCENTAGE // until starting value is reached -const MAX_WORKERS_DECREASE_PERCENTAGE = 0.8; -const MAX_WORKERS_INCREASE_PERCENTAGE = 1.05; +const CAPACITY_DECREASE_PERCENTAGE = 0.8; +const CAPACITY_INCREASE_PERCENTAGE = 1.05; // When errors occur, increase pollInterval by POLL_INTERVAL_INCREASE_PERCENTAGE // When errors no longer occur, start decreasing pollInterval by POLL_INTERVAL_DECREASE_PERCENTAGE @@ -29,28 +38,32 @@ const POLL_INTERVAL_DECREASE_PERCENTAGE = 0.95; const POLL_INTERVAL_INCREASE_PERCENTAGE = 1.2; interface ManagedConfigurationOpts { - logger: Logger; - startingMaxWorkers: number; - startingPollInterval: number; + config: TaskManagerConfig; + defaultCapacity?: number; errors$: Observable<Error>; + logger: Logger; } export interface ManagedConfiguration { - maxWorkersConfiguration$: Observable<number>; + startingCapacity: number; + capacityConfiguration$: Observable<number>; pollIntervalConfiguration$: Observable<number>; } export function createManagedConfiguration({ + config, + defaultCapacity = DEFAULT_CAPACITY, logger, - startingMaxWorkers, - startingPollInterval, errors$, }: ManagedConfigurationOpts): ManagedConfiguration { const errorCheck$ = countErrors(errors$, ADJUST_THROUGHPUT_INTERVAL); + const startingCapacity = calculateStartingCapacity(config, logger, defaultCapacity); + const startingPollInterval = config.poll_interval; return { - maxWorkersConfiguration$: errorCheck$.pipe( - createMaxWorkersScan(logger, startingMaxWorkers), - startWith(startingMaxWorkers), + startingCapacity, + capacityConfiguration$: errorCheck$.pipe( + createCapacityScan(config, logger, startingCapacity), + startWith(startingCapacity), distinctUntilChanged() ), pollIntervalConfiguration$: errorCheck$.pipe( @@ -61,37 +74,39 @@ export function createManagedConfiguration({ }; } -function createMaxWorkersScan(logger: Logger, startingMaxWorkers: number) { - return scan((previousMaxWorkers: number, errorCount: number) => { - let newMaxWorkers: number; +function createCapacityScan(config: TaskManagerConfig, logger: Logger, startingCapacity: number) { + return scan((previousCapacity: number, errorCount: number) => { + let newCapacity: number; if (errorCount > 0) { - // Decrease max workers by MAX_WORKERS_DECREASE_PERCENTAGE while making sure it doesn't go lower than 1. + const minCapacity = getMinCapacity(config); + // Decrease capacity by CAPACITY_DECREASE_PERCENTAGE while making sure it doesn't go lower than minCapacity. // Using Math.floor to make sure the number is different than previous while not being a decimal value. - newMaxWorkers = Math.max( - Math.floor(previousMaxWorkers * MAX_WORKERS_DECREASE_PERCENTAGE), - MIN_WORKERS + newCapacity = Math.max( + Math.floor(previousCapacity * CAPACITY_DECREASE_PERCENTAGE), + minCapacity ); } else { - // Increase max workers by MAX_WORKERS_INCREASE_PERCENTAGE while making sure it doesn't go + // Increase capacity by CAPACITY_INCREASE_PERCENTAGE while making sure it doesn't go // higher than the starting value. Using Math.ceil to make sure the number is different than // previous while not being a decimal value - newMaxWorkers = Math.min( - startingMaxWorkers, - Math.ceil(previousMaxWorkers * MAX_WORKERS_INCREASE_PERCENTAGE) + newCapacity = Math.min( + startingCapacity, + Math.ceil(previousCapacity * CAPACITY_INCREASE_PERCENTAGE) ); } - if (newMaxWorkers !== previousMaxWorkers) { + + if (newCapacity !== previousCapacity) { logger.debug( - `Max workers configuration changing from ${previousMaxWorkers} to ${newMaxWorkers} after seeing ${errorCount} "too many request" and/or "execute [inline] script" error(s)` + `Capacity configuration changing from ${previousCapacity} to ${newCapacity} after seeing ${errorCount} "too many request" and/or "execute [inline] script" error(s)` ); - if (previousMaxWorkers === startingMaxWorkers) { + if (previousCapacity === startingCapacity) { logger.warn( - `Max workers configuration is temporarily reduced after Elasticsearch returned ${errorCount} "too many request" and/or "execute [inline] script" error(s).` + `Capacity configuration is temporarily reduced after Elasticsearch returned ${errorCount} "too many request" and/or "execute [inline] script" error(s).` ); } } - return newMaxWorkers; - }, startingMaxWorkers); + return newCapacity; + }, startingCapacity); } function createPollIntervalScan(logger: Logger, startingPollInterval: number) { @@ -186,3 +201,36 @@ function resetErrorCount() { count: 0, }; } + +function getMinCapacity(config: TaskManagerConfig) { + switch (config.claim_strategy) { + case CLAIM_STRATEGY_MGET: + return MIN_COST; + + default: + return MIN_WORKERS; + } +} + +export function calculateStartingCapacity( + config: TaskManagerConfig, + logger: Logger, + defaultCapacity: number +): number { + if (config.capacity !== undefined && config.max_workers !== undefined) { + logger.warn( + `Both "xpack.task_manager.capacity" and "xpack.task_manager.max_workers" configs are set, max_workers will be ignored in favor of capacity and the setting should be removed.` + ); + } + + if (config.capacity) { + // Use capacity if explicitly set + return config.capacity!; + } else if (config.max_workers) { + // Otherwise use max_worker value as capacity, capped at MAX_CAPACITY + return Math.min(config.max_workers, MAX_CAPACITY); + } + + // Neither are set, use the given default capacity + return defaultCapacity; +} diff --git a/x-pack/plugins/task_manager/server/lib/fill_pool.test.ts b/x-pack/plugins/task_manager/server/lib/fill_pool.test.ts index 9fdb16fb5f677..d3533ac058314 100644 --- a/x-pack/plugins/task_manager/server/lib/fill_pool.test.ts +++ b/x-pack/plugins/task_manager/server/lib/fill_pool.test.ts @@ -30,7 +30,6 @@ describe('fillPool', () => { tasksUpdated: tasks?.length ?? 0, tasksConflicted: 0, tasksClaimed: 0, - tasksRejected: 0, }, docs: tasks, }) diff --git a/x-pack/plugins/task_manager/server/lib/get_default_capacity.test.ts b/x-pack/plugins/task_manager/server/lib/get_default_capacity.test.ts new file mode 100644 index 0000000000000..fb68a3620e43c --- /dev/null +++ b/x-pack/plugins/task_manager/server/lib/get_default_capacity.test.ts @@ -0,0 +1,185 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CLAIM_STRATEGY_DEFAULT, CLAIM_STRATEGY_MGET, DEFAULT_CAPACITY } from '../config'; +import { getDefaultCapacity } from './get_default_capacity'; + +describe('getDefaultCapacity', () => { + it('returns default capacity when not in cloud', () => { + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: false, + isServerless: false, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: false, + isServerless: true, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: false, + isServerless: false, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: false, + isServerless: true, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + }); + + it('returns default capacity when default claim strategy', () => { + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_DEFAULT, + }) + ).toBe(DEFAULT_CAPACITY); + + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_DEFAULT, + }) + ).toBe(DEFAULT_CAPACITY); + }); + + it('returns default capacity when serverless', () => { + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: false, + isServerless: true, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: false, + isServerless: true, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: true, + isServerless: true, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: true, + isServerless: true, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(DEFAULT_CAPACITY); + }); + + it('returns capacity as expected when in cloud and claim strategy is mget', () => { + // 1GB + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(10); + + // 1GB but somehow background task node only is true + expect( + getDefaultCapacity({ + heapSizeLimit: 851443712, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(10); + + // 2GB + expect( + getDefaultCapacity({ + heapSizeLimit: 1702887424, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(15); + + // 2GB but somehow background task node only is true + expect( + getDefaultCapacity({ + heapSizeLimit: 1702887424, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(15); + + // 4GB + expect( + getDefaultCapacity({ + heapSizeLimit: 3405774848, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: false, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(25); + + // 4GB background task only + expect( + getDefaultCapacity({ + heapSizeLimit: 3405774848, + isCloud: true, + isServerless: false, + isBackgroundTaskNodeOnly: true, + claimStrategy: CLAIM_STRATEGY_MGET, + }) + ).toBe(50); + }); +}); diff --git a/x-pack/plugins/task_manager/server/lib/get_default_capacity.ts b/x-pack/plugins/task_manager/server/lib/get_default_capacity.ts new file mode 100644 index 0000000000000..aeafa0f63c4d7 --- /dev/null +++ b/x-pack/plugins/task_manager/server/lib/get_default_capacity.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 { CLAIM_STRATEGY_MGET, DEFAULT_CAPACITY } from '../config'; + +interface GetDefaultCapacityOpts { + claimStrategy?: string; + heapSizeLimit: number; + isCloud: boolean; + isServerless: boolean; + isBackgroundTaskNodeOnly: boolean; +} + +// Map instance size to desired capacity +const HEAP_TO_CAPACITY_MAP = [ + { minHeap: 0, maxHeap: 1, capacity: 10 }, + { minHeap: 1, maxHeap: 2, capacity: 15 }, + { minHeap: 2, maxHeap: 4, capacity: 25, backgroundTaskNodeOnly: false }, + { minHeap: 2, maxHeap: 4, capacity: 50, backgroundTaskNodeOnly: true }, +]; + +export function getDefaultCapacity({ + claimStrategy, + heapSizeLimit: heapSizeLimitInBytes, + isCloud, + isServerless, + isBackgroundTaskNodeOnly, +}: GetDefaultCapacityOpts) { + // perform heap size based calculations only in cloud + if (isCloud && !isServerless && claimStrategy === CLAIM_STRATEGY_MGET) { + // convert bytes to GB + const heapSizeLimitInGB = heapSizeLimitInBytes / 1e9; + + const config = HEAP_TO_CAPACITY_MAP.find((map) => { + return ( + heapSizeLimitInGB > map.minHeap && + heapSizeLimitInGB <= map.maxHeap && + (map.backgroundTaskNodeOnly === undefined || + isBackgroundTaskNodeOnly === map.backgroundTaskNodeOnly) + ); + }); + + return config?.capacity ?? DEFAULT_CAPACITY; + } + + return DEFAULT_CAPACITY; +} diff --git a/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts b/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts index ea0793b60266b..a39568df5fdd2 100644 --- a/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts +++ b/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts @@ -435,7 +435,8 @@ function getMockMonitoredHealth(overrides = {}): MonitoredHealth { timestamp: new Date().toISOString(), status: HealthStatus.OK, value: { - max_workers: 10, + capacity: { config: 10, as_cost: 20, as_workers: 10 }, + claim_strategy: 'default', poll_interval: 3000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -454,16 +455,19 @@ function getMockMonitoredHealth(overrides = {}): MonitoredHealth { status: HealthStatus.OK, value: { count: 4, + cost: 8, task_types: { - actions_telemetry: { count: 2, status: { idle: 2 } }, - alerting_telemetry: { count: 1, status: { idle: 1 } }, - session_cleanup: { count: 1, status: { idle: 1 } }, + actions_telemetry: { count: 2, cost: 4, status: { idle: 2 } }, + alerting_telemetry: { count: 1, cost: 2, status: { idle: 1 } }, + session_cleanup: { count: 1, cost: 2, status: { idle: 1 } }, }, schedule: [], overdue: 0, + overdue_cost: 0, overdue_non_recurring: 0, estimatedScheduleDensity: [], non_recurring: 20, + non_recurring_cost: 40, owner_ids: 2, estimated_schedule_density: [], capacity_requirements: { diff --git a/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts b/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts index 309617a8e4cc3..b1cf9a90b6cb6 100644 --- a/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts +++ b/x-pack/plugins/task_manager/server/metrics/create_aggregator.test.ts @@ -45,7 +45,6 @@ const config: TaskManagerConfig = { warn_threshold: 5000, }, max_attempts: 9, - max_workers: 10, metrics_reset_interval: 30000, monitored_aggregated_stats_refresh_rate: 5000, monitored_stats_health_verbose_log: { diff --git a/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts index 837f29c83f108..5a9a9e07aadf7 100644 --- a/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/background_task_utilization_statistics.ts @@ -21,7 +21,7 @@ import { } from '../task_events'; import { MonitoredStat } from './monitoring_stats_stream'; import { AggregatedStat, AggregatedStatProvider } from '../lib/runtime_statistics_aggregator'; -import { createRunningAveragedStat } from './task_run_calcultors'; +import { createRunningAveragedStat } from './task_run_calculators'; import { DEFAULT_WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW } from '../config'; export interface PublicBackgroundTaskUtilizationStat extends JsonObject { diff --git a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.test.ts b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.test.ts index 263f2e9987b7c..9791ac805e500 100644 --- a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.test.ts @@ -21,7 +21,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -77,7 +77,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -135,7 +135,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -172,7 +172,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -228,7 +228,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { // 0 active tasks at this moment in time, so no owners identifiable owner_ids: 0, @@ -285,7 +285,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 3, overdue_non_recurring: 0, @@ -347,7 +347,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: provisionedKibanaInstances, overdue_non_recurring: 0, @@ -428,7 +428,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: provisionedKibanaInstances, overdue_non_recurring: 0, @@ -510,7 +510,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -578,7 +578,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -643,7 +643,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -708,7 +708,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -784,7 +784,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { owner_ids: 1, overdue_non_recurring: 0, @@ -862,7 +862,7 @@ describe('estimateCapacity', () => { estimateCapacity( logger, mockStats( - { max_workers: 10, poll_interval: 3000 }, + { capacity: { config: 10, as_cost: 20, as_workers: 10 }, poll_interval: 3000 }, { overdue: undefined, owner_ids: 1, @@ -949,7 +949,8 @@ function mockStats( status: HealthStatus.OK, timestamp: new Date().toISOString(), value: { - max_workers: 0, + capacity: { config: 10, as_cost: 20, as_workers: 10 }, + claim_strategy: 'default', poll_interval: 0, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -969,16 +970,19 @@ function mockStats( timestamp: new Date().toISOString(), value: { count: 4, + cost: 8, task_types: { - actions_telemetry: { count: 2, status: { idle: 2 } }, - alerting_telemetry: { count: 1, status: { idle: 1 } }, - session_cleanup: { count: 1, status: { idle: 1 } }, + actions_telemetry: { count: 2, cost: 4, status: { idle: 2 } }, + alerting_telemetry: { count: 1, cost: 2, status: { idle: 1 } }, + session_cleanup: { count: 1, cost: 2, status: { idle: 1 } }, }, schedule: [], overdue: 0, + overdue_cost: 0, overdue_non_recurring: 0, estimated_schedule_density: [], non_recurring: 20, + non_recurring_cost: 40, owner_ids: 2, capacity_requirements: { per_minute: 150, diff --git a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts index b12382f16e27b..d1c2f3591ea22 100644 --- a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts +++ b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts @@ -10,7 +10,7 @@ import stats from 'stats-lite'; import { JsonObject } from '@kbn/utility-types'; import { Logger } from '@kbn/core/server'; import { RawMonitoringStats, RawMonitoredStat, HealthStatus } from './monitoring_stats_stream'; -import { AveragedStat } from './task_run_calcultors'; +import { AveragedStat } from './task_run_calculators'; import { TaskPersistenceTypes } from './task_run_statistics'; import { asErr, asOk, map, Result } from '../lib/result_type'; @@ -61,8 +61,10 @@ export function estimateCapacity( non_recurring: percentageOfExecutionsUsedByNonRecurringTasks, } = capacityStats.runtime.value.execution.persistence; const { overdue, capacity_requirements: capacityRequirements } = workload; - const { poll_interval: pollInterval, max_workers: maxWorkers } = - capacityStats.configuration.value; + const { + poll_interval: pollInterval, + capacity: { config: configuredCapacity }, + } = capacityStats.configuration.value; /** * On average, how many polling cycles does it take to execute a task? @@ -78,10 +80,10 @@ export function estimateCapacity( ); /** - * Given the current configuration how much task capacity do we have? + * Given the current configuration how much capacity do we have to run normal cost tasks? */ const capacityPerMinutePerKibana = Math.round( - ((60 * 1000) / (averagePollIntervalsPerExecution * pollInterval)) * maxWorkers + ((60 * 1000) / (averagePollIntervalsPerExecution * pollInterval)) * configuredCapacity ); /** diff --git a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts index 822356e2d6534..0b5387b66dece 100644 --- a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.test.ts @@ -13,7 +13,6 @@ import { TaskManagerConfig } from '../config'; describe('Configuration Statistics Aggregator', () => { test('merges the static config with the merged configs', async () => { const configuration: TaskManagerConfig = { - max_workers: 10, max_attempts: 9, poll_interval: 6000000, allow_reading_invalid_state: false, @@ -55,7 +54,8 @@ describe('Configuration Statistics Aggregator', () => { }; const managedConfig = { - maxWorkersConfiguration$: new Subject<number>(), + startingCapacity: 10, + capacityConfiguration$: new Subject<number>(), pollIntervalConfiguration$: new Subject<number>(), }; @@ -65,7 +65,12 @@ describe('Configuration Statistics Aggregator', () => { .pipe(take(3), bufferCount(3)) .subscribe(([initial, updatedWorkers, updatedInterval]) => { expect(initial.value).toEqual({ - max_workers: 10, + capacity: { + config: 10, + as_workers: 10, + as_cost: 20, + }, + claim_strategy: 'default', poll_interval: 6000000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -79,7 +84,12 @@ describe('Configuration Statistics Aggregator', () => { }, }); expect(updatedWorkers.value).toEqual({ - max_workers: 8, + capacity: { + config: 8, + as_workers: 8, + as_cost: 16, + }, + claim_strategy: 'default', poll_interval: 6000000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -93,7 +103,12 @@ describe('Configuration Statistics Aggregator', () => { }, }); expect(updatedInterval.value).toEqual({ - max_workers: 8, + capacity: { + config: 8, + as_workers: 8, + as_cost: 16, + }, + claim_strategy: 'default', poll_interval: 3000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -108,7 +123,7 @@ describe('Configuration Statistics Aggregator', () => { }); resolve(); }, reject); - managedConfig.maxWorkersConfiguration$.next(8); + managedConfig.capacityConfiguration$.next(8); managedConfig.pollIntervalConfiguration$.next(3000); } catch (error) { reject(error); diff --git a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.ts index dc3221351a33e..c606b63694b0f 100644 --- a/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/configuration_statistics.ts @@ -8,9 +8,11 @@ import { combineLatest, of } from 'rxjs'; import { pick, merge } from 'lodash'; import { map, startWith } from 'rxjs'; +import { JsonObject } from '@kbn/utility-types'; import { AggregatedStatProvider } from '../lib/runtime_statistics_aggregator'; -import { TaskManagerConfig } from '../config'; +import { CLAIM_STRATEGY_DEFAULT, TaskManagerConfig } from '../config'; import { ManagedConfiguration } from '../lib/create_managed_configuration'; +import { getCapacityInCost, getCapacityInWorkers } from '../task_pool'; const CONFIG_FIELDS_TO_EXPOSE = [ 'request_capacity', @@ -19,10 +21,19 @@ const CONFIG_FIELDS_TO_EXPOSE = [ 'monitored_task_execution_thresholds', ] as const; +interface CapacityConfig extends JsonObject { + capacity: { + config: number; + as_workers: number; + as_cost: number; + }; +} + export type ConfigStat = Pick< TaskManagerConfig, - 'max_workers' | 'poll_interval' | (typeof CONFIG_FIELDS_TO_EXPOSE)[number] ->; + 'poll_interval' | 'claim_strategy' | (typeof CONFIG_FIELDS_TO_EXPOSE)[number] +> & + CapacityConfig; export function createConfigurationAggregator( config: TaskManagerConfig, @@ -30,16 +41,21 @@ export function createConfigurationAggregator( ): AggregatedStatProvider<ConfigStat> { return combineLatest([ of(pick(config, ...CONFIG_FIELDS_TO_EXPOSE)), + of({ claim_strategy: config.claim_strategy ?? CLAIM_STRATEGY_DEFAULT }), managedConfig.pollIntervalConfiguration$.pipe( startWith(config.poll_interval), map<number, Pick<TaskManagerConfig, 'poll_interval'>>((pollInterval) => ({ poll_interval: pollInterval, })) ), - managedConfig.maxWorkersConfiguration$.pipe( - startWith(config.max_workers), - map<number, Pick<TaskManagerConfig, 'max_workers'>>((maxWorkers) => ({ - max_workers: maxWorkers, + managedConfig.capacityConfiguration$.pipe( + startWith(managedConfig.startingCapacity), + map<number, CapacityConfig>((capacity) => ({ + capacity: { + config: capacity, + as_workers: getCapacityInWorkers(capacity), + as_cost: getCapacityInCost(capacity), + }, })) ), ]).pipe( diff --git a/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.test.ts index d7135837e052e..ac16070d7c131 100644 --- a/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.test.ts @@ -176,11 +176,11 @@ describe('Ephemeral Task Statistics', () => { }); const runningAverageWindowSize = 5; - const maxWorkers = 10; + const capacity = 10; const ephemeralTaskAggregator = createEphemeralTaskAggregator( ephemeralTaskLifecycle, runningAverageWindowSize, - maxWorkers + capacity ); function expectWindowEqualsUpdate( @@ -229,7 +229,7 @@ describe('Ephemeral Task Statistics', () => { }); }); -test('returns the average load added per polling cycle cycle by ephemeral tasks when load exceeds max workers', async () => { +test('returns the average load added per polling cycle cycle by ephemeral tasks when load exceeds capacity', async () => { const tasksExecuted = [0, 5, 10, 20, 15, 10, 5, 0, 0, 0, 0, 0]; const expectedLoad = [0, 50, 100, 200, 150, 100, 50, 0, 0, 0, 0, 0]; @@ -241,11 +241,11 @@ test('returns the average load added per polling cycle cycle by ephemeral tasks }); const runningAverageWindowSize = 5; - const maxWorkers = 10; + const capacity = 10; const ephemeralTaskAggregator = createEphemeralTaskAggregator( ephemeralTaskLifecycle, runningAverageWindowSize, - maxWorkers + capacity ); function expectWindowEqualsUpdate( diff --git a/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.ts index b77eae1080fbc..d02080a56a1aa 100644 --- a/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/ephemeral_task_statistics.ts @@ -17,7 +17,7 @@ import { AveragedStat, calculateRunningAverage, createRunningAveragedStat, -} from './task_run_calcultors'; +} from './task_run_calculators'; import { HealthStatus } from './monitoring_stats_stream'; export interface EphemeralTaskStat extends JsonObject { @@ -35,7 +35,7 @@ export interface SummarizedEphemeralTaskStat extends JsonObject { export function createEphemeralTaskAggregator( ephemeralTaskLifecycle: EphemeralTaskLifecycle, runningAverageWindowSize: number, - maxWorkers: number + capacity: number ): AggregatedStatProvider<EphemeralTaskStat> { const ephemeralTaskRunEvents$ = ephemeralTaskLifecycle.events.pipe( filter((taskEvent: TaskLifecycleEvent) => isTaskRunEvent(taskEvent)) @@ -70,7 +70,7 @@ export function createEphemeralTaskAggregator( map(([tasksRanSincePreviousQueueSize, ephemeralQueueSize]) => ({ queuedTasks: ephemeralQueuedTasksQueue(ephemeralQueueSize), executionsPerCycle: ephemeralQueueExecutionsPerCycleQueue(tasksRanSincePreviousQueueSize), - load: ephemeralTaskLoadQueue(calculateWorkerLoad(maxWorkers, tasksRanSincePreviousQueueSize)), + load: ephemeralTaskLoadQueue(calculateWorkerLoad(capacity, tasksRanSincePreviousQueueSize)), })), startWith({ queuedTasks: [], diff --git a/x-pack/plugins/task_manager/server/monitoring/index.ts b/x-pack/plugins/task_manager/server/monitoring/index.ts index 9ee32e97d7758..5dc024b53de10 100644 --- a/x-pack/plugins/task_manager/server/monitoring/index.ts +++ b/x-pack/plugins/task_manager/server/monitoring/index.ts @@ -18,6 +18,7 @@ import { TaskPollingLifecycle } from '../polling_lifecycle'; import { ManagedConfiguration } from '../lib/create_managed_configuration'; import { EphemeralTaskLifecycle } from '../ephemeral_task_lifecycle'; import { AdHocTaskCounter } from '../lib/adhoc_task_counter'; +import { TaskTypeDictionary } from '../task_type_dictionary'; export type { MonitoringStats, RawMonitoringStats } from './monitoring_stats_stream'; export { @@ -27,27 +28,20 @@ export { createMonitoringStatsStream, } from './monitoring_stats_stream'; +export interface CreateMonitoringStatsOpts { + taskStore: TaskStore; + elasticsearchAndSOAvailability$: Observable<boolean>; + config: TaskManagerConfig; + managedConfig: ManagedConfiguration; + logger: Logger; + adHocTaskCounter: AdHocTaskCounter; + taskDefinitions: TaskTypeDictionary; + taskPollingLifecycle?: TaskPollingLifecycle; + ephemeralTaskLifecycle?: EphemeralTaskLifecycle; +} + export function createMonitoringStats( - taskStore: TaskStore, - elasticsearchAndSOAvailability$: Observable<boolean>, - config: TaskManagerConfig, - managedConfig: ManagedConfiguration, - logger: Logger, - adHocTaskCounter: AdHocTaskCounter, - taskPollingLifecycle?: TaskPollingLifecycle, - ephemeralTaskLifecycle?: EphemeralTaskLifecycle + opts: CreateMonitoringStatsOpts ): Observable<MonitoringStats> { - return createMonitoringStatsStream( - createAggregators( - taskStore, - elasticsearchAndSOAvailability$, - config, - managedConfig, - logger, - adHocTaskCounter, - taskPollingLifecycle, - ephemeralTaskLifecycle - ), - config - ); + return createMonitoringStatsStream(createAggregators(opts)); } diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts index f4da53871ffa3..075b663e4ce83 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { TaskManagerConfig } from '../config'; import { of, Subject } from 'rxjs'; import { take, bufferCount } from 'rxjs'; import { createMonitoringStatsStream } from './monitoring_stats_stream'; @@ -17,51 +16,9 @@ beforeEach(() => { }); describe('createMonitoringStatsStream', () => { - const configuration: TaskManagerConfig = { - max_workers: 10, - max_attempts: 9, - poll_interval: 6000000, - allow_reading_invalid_state: false, - version_conflict_threshold: 80, - monitored_stats_required_freshness: 6000000, - request_capacity: 1000, - monitored_aggregated_stats_refresh_rate: 5000, - monitored_stats_health_verbose_log: { - enabled: false, - level: 'debug' as const, - warn_delayed_task_start_in_seconds: 60, - }, - monitored_stats_running_average_window: 50, - monitored_task_execution_thresholds: { - default: { - error_threshold: 90, - warn_threshold: 80, - }, - custom: {}, - }, - ephemeral_tasks: { - enabled: true, - request_capacity: 10, - }, - unsafe: { - exclude_task_types: [], - authenticate_background_task_utilization: true, - }, - event_loop_delay: { - monitor: true, - warn_threshold: 5000, - }, - worker_utilization_running_average_window: 5, - metrics_reset_interval: 3000, - claim_strategy: 'default', - request_timeouts: { - update_by_query: 1000, - }, - }; - it('returns the initial config used to configure Task Manager', async () => { return new Promise<void>((resolve) => { - createMonitoringStatsStream(of(), configuration) + createMonitoringStatsStream(of()) .pipe(take(1)) .subscribe((firstValue) => { expect(firstValue.stats).toEqual({}); @@ -74,7 +31,7 @@ describe('createMonitoringStatsStream', () => { const aggregatedStats$ = new Subject<AggregatedStat>(); return new Promise<void>((resolve) => { - createMonitoringStatsStream(aggregatedStats$, configuration) + createMonitoringStatsStream(aggregatedStats$) .pipe(take(3), bufferCount(3)) .subscribe(([initialValue, secondValue, thirdValue]) => { expect(initialValue.stats).toMatchObject({ @@ -82,7 +39,7 @@ describe('createMonitoringStatsStream', () => { stats: { configuration: { value: { - max_workers: 10, + capacity: 10, poll_interval: 6000000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -115,7 +72,7 @@ describe('createMonitoringStatsStream', () => { configuration: { timestamp: expect.any(String), value: { - max_workers: 10, + capacity: 10, poll_interval: 6000000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -148,7 +105,7 @@ describe('createMonitoringStatsStream', () => { configuration: { timestamp: expect.any(String), value: { - max_workers: 10, + capacity: 10, poll_interval: 6000000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts index 5ee6465dae0eb..e1bffb55d54fa 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts @@ -10,8 +10,6 @@ import { map, scan } from 'rxjs'; import { set } from '@kbn/safer-lodash-set'; import { Logger } from '@kbn/core/server'; import { JsonObject } from '@kbn/utility-types'; -import { TaskStore } from '../task_store'; -import { TaskPollingLifecycle } from '../polling_lifecycle'; import { createWorkloadAggregator, summarizeWorkloadStat, @@ -37,11 +35,9 @@ import { import { ConfigStat, createConfigurationAggregator } from './configuration_statistics'; import { TaskManagerConfig } from '../config'; -import { ManagedConfiguration } from '../lib/create_managed_configuration'; -import { EphemeralTaskLifecycle } from '../ephemeral_task_lifecycle'; import { CapacityEstimationStat, withCapacityEstimate } from './capacity_estimation'; -import { AdHocTaskCounter } from '../lib/adhoc_task_counter'; import { AggregatedStatProvider } from '../lib/runtime_statistics_aggregator'; +import { CreateMonitoringStatsOpts } from '.'; export interface MonitoringStats { last_update: string; @@ -81,26 +77,28 @@ export interface RawMonitoringStats { }; } -export function createAggregators( - taskStore: TaskStore, - elasticsearchAndSOAvailability$: Observable<boolean>, - config: TaskManagerConfig, - managedConfig: ManagedConfiguration, - logger: Logger, - adHocTaskCounter: AdHocTaskCounter, - taskPollingLifecycle?: TaskPollingLifecycle, - ephemeralTaskLifecycle?: EphemeralTaskLifecycle -): AggregatedStatProvider { +export function createAggregators({ + taskStore, + elasticsearchAndSOAvailability$, + config, + managedConfig, + logger, + taskDefinitions, + adHocTaskCounter, + taskPollingLifecycle, + ephemeralTaskLifecycle, +}: CreateMonitoringStatsOpts): AggregatedStatProvider { const aggregators: AggregatedStatProvider[] = [ createConfigurationAggregator(config, managedConfig), - createWorkloadAggregator( + createWorkloadAggregator({ taskStore, elasticsearchAndSOAvailability$, - config.monitored_aggregated_stats_refresh_rate, - config.poll_interval, - logger - ), + refreshInterval: config.monitored_aggregated_stats_refresh_rate, + pollInterval: config.poll_interval, + logger, + taskDefinitions, + }), ]; if (taskPollingLifecycle) { aggregators.push( @@ -118,7 +116,7 @@ export function createAggregators( createEphemeralTaskAggregator( ephemeralTaskLifecycle, config.monitored_stats_running_average_window, - config.max_workers + managedConfig.startingCapacity ) ); } @@ -126,8 +124,7 @@ export function createAggregators( } export function createMonitoringStatsStream( - provider$: AggregatedStatProvider, - config: TaskManagerConfig + provider$: AggregatedStatProvider ): Observable<MonitoringStats> { const initialStats = { last_update: new Date().toISOString(), diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.test.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_calculators.test.ts similarity index 98% rename from x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.test.ts rename to x-pack/plugins/task_manager/server/monitoring/task_run_calculators.test.ts index b5f6be8b7524d..46df2b1b21d42 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_calculators.test.ts @@ -12,7 +12,7 @@ import { calculateFrequency, createRunningAveragedStat, createMapOfRunningAveragedStats, -} from './task_run_calcultors'; +} from './task_run_calculators'; describe('calculateRunningAverage', () => { test('calculates the running average and median of a window of values', async () => { diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_calculators.ts similarity index 100% rename from x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.ts rename to x-pack/plugins/task_manager/server/monitoring/task_run_calculators.ts diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts index 6a7f10b7e75b6..517b29a54cd64 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts @@ -35,7 +35,7 @@ import { calculateFrequency, createRunningAveragedStat, createMapOfRunningAveragedStats, -} from './task_run_calcultors'; +} from './task_run_calculators'; import { HealthStatus } from './monitoring_stats_stream'; import { TaskPollingLifecycle } from '../polling_lifecycle'; import { TaskExecutionFailureThreshold, TaskManagerConfig } from '../config'; diff --git a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts index 7ef860efa783a..cd37c6661ec00 100644 --- a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.test.ts @@ -15,13 +15,14 @@ import { padBuckets, estimateRecurringTaskScheduling, } from './workload_statistics'; -import { ConcreteTaskInstance } from '../task'; +import { ConcreteTaskInstance, TaskCost } from '../task'; import { times } from 'lodash'; import { taskStoreMock } from '../task_store.mock'; import { of, Subject } from 'rxjs'; import { sleep } from '../test_utils'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { TaskTypeDictionary } from '../task_type_dictionary'; type ResponseWithAggs = Omit<estypes.SearchResponse<ConcreteTaskInstance>, 'aggregations'> & { aggregations: WorkloadAggregationResponse; @@ -32,52 +33,98 @@ const asApiResponse = (body: ResponseWithAggs) => .createSuccessTransportRequestPromise(body as estypes.SearchResponse<ConcreteTaskInstance>) .then((res) => res.body as ResponseWithAggs); +const logger = loggingSystemMock.create().get(); + +const definitions = new TaskTypeDictionary(logger); +definitions.registerTaskDefinitions({ + report: { + title: 'report', + cost: TaskCost.ExtraLarge, + createTaskRunner: jest.fn(), + }, + foo: { + title: 'foo', + createTaskRunner: jest.fn(), + }, + bar: { + title: 'bar', + cost: TaskCost.Tiny, + createTaskRunner: jest.fn(), + }, +}); describe('Workload Statistics Aggregator', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + test('queries the Task Store at a fixed interval for the current workload', async () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate.mockResolvedValue( asApiResponse({ - hits: { - hits: [], - max_score: 0, - total: { value: 0, relation: 'eq' }, - }, + hits: { hits: [], max_score: 0, total: { value: 3, relation: 'eq' } }, took: 1, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 1, - failed: 0, - }, + _shards: { total: 1, successful: 1, skipped: 1, failed: 0 }, aggregations: { taskType: { - buckets: [], + buckets: [ + { + key: 'foo', + doc_count: 1, + status: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'idle', doc_count: 1 }], + }, + }, + { + key: 'bar', + doc_count: 1, + status: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'claiming', doc_count: 1 }], + }, + }, + { + key: 'report', + doc_count: 1, + status: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'idle', doc_count: 1 }], + }, + }, + ], doc_count_error_upper_bound: 0, sum_other_doc_count: 0, }, schedule: { - buckets: [], + buckets: [{ key: '1m', doc_count: 8 }], doc_count_error_upper_bound: 0, sum_other_doc_count: 0, }, nonRecurringTasks: { - doc_count: 13, - }, - ownerIds: { - ownerIds: { - value: 1, + doc_count: 1, + taskType: { + buckets: [{ key: 'report', doc_count: 1 }], + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, }, }, + ownerIds: { ownerIds: { value: 1 } }, // The `FiltersAggregate` doesn't cover the case of a nested `AggregationsAggregationContainer`, in which `FiltersAggregate` // would not have a `buckets` property, but rather a keyed property that's inferred from the request. // @ts-expect-error idleTasks: { doc_count: 0, overdue: { - doc_count: 0, - nonRecurring: { - doc_count: 0, + doc_count: 1, + nonRecurring: { doc_count: 0 }, + taskTypes: { + buckets: [{ key: 'foo', doc_count: 1 }], + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, }, }, scheduleDensity: { @@ -89,9 +136,7 @@ describe('Workload Statistics Aggregator', () => { to: 1.601651976274e12, to_as_string: '2020-10-02T15:19:36.274Z', doc_count: 0, - histogram: { - buckets: [], - }, + histogram: { buckets: [] }, }, ], }, @@ -100,87 +145,51 @@ describe('Workload Statistics Aggregator', () => { }) ); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), - 10, - 3000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve) => { workloadAggregator.pipe(first()).subscribe(() => { expect(taskStore.aggregate).toHaveBeenCalledWith({ aggs: { taskType: { - terms: { size: 100, field: 'task.taskType' }, - aggs: { - status: { - terms: { field: 'task.status' }, - }, - }, + terms: { size: 3, field: 'task.taskType' }, + aggs: { status: { terms: { field: 'task.status' } } }, }, schedule: { - terms: { - field: 'task.schedule.interval', - size: 100, - }, + terms: { field: 'task.schedule.interval', size: 100 }, }, nonRecurringTasks: { - missing: { field: 'task.schedule' }, + missing: { field: 'task.schedule.interval' }, + aggs: { taskType: { terms: { size: 3, field: 'task.taskType' } } }, }, ownerIds: { - filter: { - range: { - 'task.startedAt': { - gte: 'now-1w/w', - }, - }, - }, - aggs: { - ownerIds: { - cardinality: { - field: 'task.ownerId', - }, - }, - }, + filter: { range: { 'task.startedAt': { gte: 'now-1w/w' } } }, + aggs: { ownerIds: { cardinality: { field: 'task.ownerId' } } }, }, idleTasks: { - filter: { - term: { 'task.status': 'idle' }, - }, + filter: { term: { 'task.status': 'idle' } }, aggs: { scheduleDensity: { - range: { - field: 'task.runAt', - ranges: [{ from: 'now', to: 'now+1m' }], - }, + range: { field: 'task.runAt', ranges: [{ from: 'now', to: 'now+1m' }] }, aggs: { histogram: { - date_histogram: { - field: 'task.runAt', - fixed_interval: '3s', - }, - aggs: { - interval: { - terms: { - field: 'task.schedule.interval', - }, - }, - }, + date_histogram: { field: 'task.runAt', fixed_interval: '3s' }, + aggs: { interval: { terms: { field: 'task.schedule.interval' } } }, }, }, }, overdue: { - filter: { - range: { - 'task.runAt': { lt: 'now' }, - }, - }, + filter: { range: { 'task.runAt': { lt: 'now' } } }, aggs: { - nonRecurring: { - missing: { field: 'task.schedule' }, - }, + nonRecurring: { missing: { field: 'task.schedule.interval' } }, + taskTypes: { terms: { size: 3, field: 'task.taskType' } }, }, }, }, @@ -192,137 +201,189 @@ describe('Workload Statistics Aggregator', () => { }); }); - const mockAggregatedResult = () => - asApiResponse({ - hits: { - hits: [], - max_score: 0, - total: { value: 4, relation: 'eq' }, + const mockResult = (overrides = {}): ResponseWithAggs => ({ + hits: { hits: [], max_score: 0, total: { value: 4, relation: 'eq' } }, + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 1, failed: 0 }, + aggregations: { + schedule: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: '3600s', doc_count: 1 }, + { key: '60s', doc_count: 1 }, + { key: '720m', doc_count: 1 }, + ], }, - took: 1, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 1, - failed: 0, + taskType: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'foo', + doc_count: 2, + status: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'idle', doc_count: 2 }], + }, + }, + { + key: 'bar', + doc_count: 1, + status: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'idle', doc_count: 1 }], + }, + }, + { + key: 'report', + doc_count: 1, + status: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'idle', doc_count: 1 }], + }, + }, + ], }, - aggregations: { - schedule: { + nonRecurringTasks: { + doc_count: 1, + taskType: { + buckets: [{ key: 'report', doc_count: 1 }], doc_count_error_upper_bound: 0, sum_other_doc_count: 0, + }, + }, + ownerIds: { ownerIds: { value: 1 } }, + // The `FiltersAggregate` doesn't cover the case of a nested `AggregationsAggregationContainer`, in which `FiltersAggregate` + // would not have a `buckets` property, but rather a keyed property that's inferred from the request. + // @ts-expect-error + idleTasks: { + doc_count: 3, + overdue: { + doc_count: 2, + nonRecurring: { doc_count: 1 }, + taskTypes: { + buckets: [{ key: 'foo', doc_count: 1 }], + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + }, + }, + scheduleDensity: { buckets: [ - { - key: '3600s', - doc_count: 1, - }, - { - key: '60s', - doc_count: 1, - }, - { - key: '720m', - doc_count: 1, - }, + mockHistogram(0, 7 * 3000 + 500, 60 * 1000, 3000, [2, 2, 5, 0, 0, 0, 0, 0, 0, 1]), ], }, + }, + ...overrides, + }, + }); + + const mockAggregatedResult = () => asApiResponse(mockResult()); + const mockAggregatedResultWithUnknownTaskType = () => + asApiResponse( + mockResult({ taskType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ { - key: 'actions_telemetry', + key: 'unknownType', + doc_count: 1, + status: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'idle', doc_count: 1 }], + }, + }, + { + key: 'foo', doc_count: 2, status: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, - buckets: [ - { - key: 'idle', - doc_count: 2, - }, - ], + buckets: [{ key: 'idle', doc_count: 2 }], }, }, { - key: 'alerting_telemetry', + key: 'bar', doc_count: 1, status: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, - buckets: [ - { - key: 'idle', - doc_count: 1, - }, - ], + buckets: [{ key: 'idle', doc_count: 1 }], }, }, { - key: 'session_cleanup', + key: 'report', doc_count: 1, status: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, - buckets: [ - { - key: 'idle', - doc_count: 1, - }, - ], + buckets: [{ key: 'idle', doc_count: 1 }], }, }, ], }, - nonRecurringTasks: { - doc_count: 13, - }, - ownerIds: { - ownerIds: { - value: 1, - }, - }, - // The `FiltersAggregate` doesn't cover the case of a nested `AggregationsAggregationContainer`, in which `FiltersAggregate` - // would not have a `buckets` property, but rather a keyed property that's inferred from the request. - // @ts-expect-error - idleTasks: { - doc_count: 13, - overdue: { - doc_count: 6, - nonRecurring: { - doc_count: 6, - }, - }, - scheduleDensity: { - buckets: [ - mockHistogram(0, 7 * 3000 + 500, 60 * 1000, 3000, [2, 2, 5, 0, 0, 0, 0, 0, 0, 1]), - ], - }, - }, - }, - }); + }) + ); test('returns a summary of the workload by task type', async () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate.mockResolvedValue(mockAggregatedResult()); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), - 10, - 3000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); + + return new Promise<void>((resolve) => { + workloadAggregator.pipe(first()).subscribe((result) => { + expect(result.key).toEqual('workload'); + expect(result.value).toMatchObject({ + count: 4, + cost: 15, + task_types: { + foo: { count: 2, cost: 4, status: { idle: 2 } }, + bar: { count: 1, cost: 1, status: { idle: 1 } }, + report: { count: 1, cost: 10, status: { idle: 1 } }, + }, + }); + resolve(); + }); + }); + }); + + test('excludes unregistered task types from the summary', async () => { + const taskStore = taskStoreMock.create({}); + taskStore.aggregate.mockResolvedValue(mockAggregatedResultWithUnknownTaskType()); + + const workloadAggregator = createWorkloadAggregator({ + taskStore, + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve) => { workloadAggregator.pipe(first()).subscribe((result) => { expect(result.key).toEqual('workload'); expect(result.value).toMatchObject({ count: 4, + cost: 15, task_types: { - actions_telemetry: { count: 2, status: { idle: 2 } }, - alerting_telemetry: { count: 1, status: { idle: 1 } }, - session_cleanup: { count: 1, status: { idle: 1 } }, + foo: { count: 2, cost: 4, status: { idle: 2 } }, + bar: { count: 1, cost: 1, status: { idle: 1 } }, + report: { count: 1, cost: 10, status: { idle: 1 } }, }, }); resolve(); @@ -336,13 +397,14 @@ describe('Workload Statistics Aggregator', () => { const availability$ = new Subject<boolean>(); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - availability$, - 10, - 3000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>(async (resolve, reject) => { try { @@ -350,25 +412,11 @@ describe('Workload Statistics Aggregator', () => { expect(result.key).toEqual('workload'); expect(result.value).toMatchObject({ count: 4, + cost: 15, task_types: { - actions_telemetry: { - count: 2, - status: { - idle: 2, - }, - }, - alerting_telemetry: { - count: 1, - status: { - idle: 1, - }, - }, - session_cleanup: { - count: 1, - status: { - idle: 1, - }, - }, + foo: { count: 2, cost: 4, status: { idle: 2 } }, + bar: { count: 1, cost: 1, status: { idle: 1 } }, + report: { count: 1, cost: 10, status: { idle: 1 } }, }, }); resolve(); @@ -389,19 +437,22 @@ describe('Workload Statistics Aggregator', () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate.mockResolvedValue(mockAggregatedResult()); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), - 10, - 3000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve) => { workloadAggregator.pipe(first()).subscribe((result) => { expect(result.key).toEqual('workload'); expect(result.value).toMatchObject({ - overdue: 6, + overdue: 2, + overdue_cost: 2, + overdue_non_recurring: 1, }); resolve(); }); @@ -412,13 +463,14 @@ describe('Workload Statistics Aggregator', () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate.mockResolvedValue(mockAggregatedResult()); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), - 10, - 3000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve) => { workloadAggregator.pipe(first()).subscribe((result) => { @@ -440,13 +492,14 @@ describe('Workload Statistics Aggregator', () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate.mockResolvedValue(mockAggregatedResult()); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), - 60 * 1000, - 3000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 60 * 1000, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve) => { workloadAggregator.pipe(first()).subscribe(() => { @@ -478,13 +531,14 @@ describe('Workload Statistics Aggregator', () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate.mockResolvedValue(mockAggregatedResult()); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), - 15 * 60 * 1000, - 3000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 15 * 60 * 1000, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve) => { workloadAggregator.pipe(first()).subscribe((result) => { @@ -517,42 +571,41 @@ describe('Workload Statistics Aggregator', () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate .mockResolvedValueOnce( - mockAggregatedResult().then((res) => - setTaskTypeCount(res, 'alerting_telemetry', { - idle: 2, - }) - ) + mockAggregatedResult().then((res) => setTaskTypeCount(res, 'foo', { idle: 2 })) ) .mockRejectedValueOnce(new Error('Elasticsearch has gone poof')) .mockResolvedValueOnce( - mockAggregatedResult().then((res) => - setTaskTypeCount(res, 'alerting_telemetry', { - idle: 1, - failed: 1, - }) - ) + mockAggregatedResult().then((res) => setTaskTypeCount(res, 'foo', { idle: 1, failed: 1 })) ); - const logger = loggingSystemMock.create().get(); - const workloadAggregator = createWorkloadAggregator(taskStore, of(true), 10, 3000, logger); + const workloadAggregator = createWorkloadAggregator({ + taskStore, + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve, reject) => { workloadAggregator.pipe(take(2), bufferCount(2)).subscribe((results) => { expect(results[0].key).toEqual('workload'); expect(results[0].value).toMatchObject({ - count: 5, + count: 4, + cost: 15, task_types: { - actions_telemetry: { count: 2, status: { idle: 2 } }, - alerting_telemetry: { count: 2, status: { idle: 2 } }, - session_cleanup: { count: 1, status: { idle: 1 } }, + bar: { count: 1, cost: 1, status: { idle: 1 } }, + report: { count: 1, cost: 10, status: { idle: 1 } }, + foo: { count: 2, cost: 4, status: { idle: 2 } }, }, }); expect(results[1].key).toEqual('workload'); expect(results[1].value).toMatchObject({ - count: 5, + count: 4, + cost: 15, task_types: { - actions_telemetry: { count: 2, status: { idle: 2 } }, - alerting_telemetry: { count: 2, status: { idle: 1, failed: 1 } }, - session_cleanup: { count: 1, status: { idle: 1 } }, + bar: { count: 1, cost: 1, status: { idle: 1 } }, + report: { count: 1, cost: 10, status: { idle: 1 } }, + foo: { count: 2, cost: 4, status: { idle: 1, failed: 1 } }, }, }); resolve(); @@ -567,49 +620,27 @@ describe('Workload Statistics Aggregator', () => { const taskStore = taskStoreMock.create({}); taskStore.aggregate.mockResolvedValue( asApiResponse({ - hits: { - hits: [], - max_score: 0, - total: { value: 4, relation: 'eq' }, - }, + hits: { hits: [], max_score: 0, total: { value: 4, relation: 'eq' } }, took: 1, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 1, - failed: 0, - }, + _shards: { total: 1, successful: 1, skipped: 1, failed: 0 }, aggregations: { schedule: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ // repeats each cycle - { - key: `${pollingIntervalInSeconds}s`, - doc_count: 1, - }, - { - key: `10s`, // 6 times per minute - doc_count: 20, - }, - { - key: `60s`, // 1 times per minute - doc_count: 10, - }, - { - key: '15m', // 4 times per hour - doc_count: 90, - }, - { - key: '720m', // 2 times per day - doc_count: 10, - }, - { - key: '3h', // 8 times per day - doc_count: 100, - }, + { key: `${pollingIntervalInSeconds}s`, doc_count: 1 }, + // 6 times per minute + { key: `10s`, doc_count: 20 }, + // 1 times per minute + { key: `60s`, doc_count: 10 }, + // 4 times per hour + { key: '15m', doc_count: 90 }, + // 2 times per day + { key: '720m', doc_count: 10 }, + // 8 times per day + { key: '3h', doc_count: 100 }, ], }, taskType: { @@ -619,12 +650,13 @@ describe('Workload Statistics Aggregator', () => { }, nonRecurringTasks: { doc_count: 13, - }, - ownerIds: { - ownerIds: { - value: 3, + taskType: { + buckets: [{ key: 'report', doc_count: 13 }], + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, }, }, + ownerIds: { ownerIds: { value: 3 } }, // The `FiltersAggregate` doesn't cover the case of a nested `AggregationContainer`, in which `FiltersAggregate` // would not have a `buckets` property, but rather a keyed property that's inferred from the request. // @ts-expect-error @@ -632,8 +664,11 @@ describe('Workload Statistics Aggregator', () => { doc_count: 13, overdue: { doc_count: 6, - nonRecurring: { - doc_count: 0, + nonRecurring: { doc_count: 0 }, + taskTypes: { + buckets: [{ key: 'foo', doc_count: 6 }], + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, }, }, scheduleDensity: { @@ -646,13 +681,14 @@ describe('Workload Statistics Aggregator', () => { }) ); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), - 10, - pollingIntervalInSeconds * 1000, - loggingSystemMock.create().get() - ); + elasticsearchAndSOAvailability$: of(true), + refreshInterval: 10, + pollInterval: pollingIntervalInSeconds * 1000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve) => { workloadAggregator.pipe(first()).subscribe((result) => { @@ -660,7 +696,7 @@ describe('Workload Statistics Aggregator', () => { expect(result.value).toMatchObject({ capacity_requirements: { - // these are buckets of required capacity, rather than aggregated requirmenets. + // these are buckets of required capacity, rather than aggregated requirements. per_minute: 150, per_hour: 360, per_day: 820, @@ -675,14 +711,14 @@ describe('Workload Statistics Aggregator', () => { const refreshInterval = 1000; const taskStore = taskStoreMock.create({}); - const logger = loggingSystemMock.create().get(); - const workloadAggregator = createWorkloadAggregator( + const workloadAggregator = createWorkloadAggregator({ taskStore, - of(true), + elasticsearchAndSOAvailability$: of(true), refreshInterval, - 3000, - logger - ); + pollInterval: 3000, + logger, + taskDefinitions: definitions, + }); return new Promise<void>((resolve, reject) => { let errorWasThrowAt = 0; @@ -694,9 +730,7 @@ describe('Workload Statistics Aggregator', () => { reject(new Error(`Elasticsearch is still poof`)); } - return setTaskTypeCount(await mockAggregatedResult(), 'alerting_telemetry', { - idle: 2, - }); + return setTaskTypeCount(await mockAggregatedResult(), 'foo', { idle: 2 }); }); workloadAggregator.pipe(take(2), bufferCount(2)).subscribe((results) => { @@ -799,7 +833,7 @@ describe('estimateRecurringTaskScheduling', () => { }); describe('padBuckets', () => { - test('returns zeroed out bucklets when there are no buckets in the histogram', async () => { + test('returns zeroed out buckets when there are no buckets in the histogram', async () => { expect( padBuckets(10, 3000, { key: '2020-10-02T19:47:28.128Z-2020-10-02T19:48:28.128Z', diff --git a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts index 6c372ce0fc453..b62ca8e8169e9 100644 --- a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts @@ -16,7 +16,9 @@ import { AggregatedStatProvider } from '../lib/runtime_statistics_aggregator'; import { parseIntervalAsSecond, asInterval, parseIntervalAsMillisecond } from '../lib/intervals'; import { HealthStatus } from './monitoring_stats_stream'; import { TaskStore } from '../task_store'; -import { createRunningAveragedStat } from './task_run_calcultors'; +import { createRunningAveragedStat } from './task_run_calculators'; +import { TaskTypeDictionary } from '../task_type_dictionary'; +import { TaskCost } from '../task'; interface StatusStat extends JsonObject { [status: string]: number; @@ -24,16 +26,20 @@ interface StatusStat extends JsonObject { interface TaskTypeStat extends JsonObject { [taskType: string]: { count: number; + cost: number; status: StatusStat; }; } interface RawWorkloadStat extends JsonObject { count: number; + cost: number; task_types: TaskTypeStat; schedule: Array<[string, number]>; non_recurring: number; + non_recurring_cost: number; overdue: number; + overdue_cost: number; overdue_non_recurring: number; estimated_schedule_density: number[]; capacity_requirements: CapacityRequirements; @@ -109,22 +115,34 @@ type ScheduleDensityResult = AggregationResultOf< type ScheduledIntervals = ScheduleDensityResult['histogram']['buckets'][0]; // Set an upper bound just in case a customer sets a really high refresh rate -const MAX_SHCEDULE_DENSITY_BUCKETS = 50; +const MAX_SCHEDULE_DENSITY_BUCKETS = 50; + +interface CreateWorkloadAggregatorOpts { + taskStore: TaskStore; + elasticsearchAndSOAvailability$: Observable<boolean>; + refreshInterval: number; + pollInterval: number; + logger: Logger; + taskDefinitions: TaskTypeDictionary; +} -export function createWorkloadAggregator( - taskStore: TaskStore, - elasticsearchAndSOAvailability$: Observable<boolean>, - refreshInterval: number, - pollInterval: number, - logger: Logger -): AggregatedStatProvider<WorkloadStat> { +export function createWorkloadAggregator({ + taskStore, + elasticsearchAndSOAvailability$, + refreshInterval, + pollInterval, + logger, + taskDefinitions, +}: CreateWorkloadAggregatorOpts): AggregatedStatProvider<WorkloadStat> { // calculate scheduleDensity going two refreshIntervals or 1 minute into into the future // (the longer of the two) const scheduleDensityBuckets = Math.min( Math.max(Math.round(60000 / pollInterval), Math.round((refreshInterval * 2) / pollInterval)), - MAX_SHCEDULE_DENSITY_BUCKETS + MAX_SCHEDULE_DENSITY_BUCKETS ); + const totalNumTaskDefinitions = taskDefinitions.getAllTypes().length; + const taskTypeTermAggSize = Math.min(totalNumTaskDefinitions, 10000); const ownerIdsQueue = createRunningAveragedStat<number>(scheduleDensityBuckets); return combineLatest([timer(0, refreshInterval), elasticsearchAndSOAvailability$]).pipe( @@ -133,39 +151,24 @@ export function createWorkloadAggregator( taskStore.aggregate({ aggs: { taskType: { - terms: { size: 100, field: 'task.taskType' }, - aggs: { - status: { - terms: { field: 'task.status' }, - }, - }, + terms: { size: taskTypeTermAggSize, field: 'task.taskType' }, + aggs: { status: { terms: { field: 'task.status' } } }, }, schedule: { terms: { field: 'task.schedule.interval', size: 100 }, }, nonRecurringTasks: { - missing: { field: 'task.schedule' }, - }, - ownerIds: { - filter: { - range: { - 'task.startedAt': { - gte: 'now-1w/w', - }, - }, - }, + missing: { field: 'task.schedule.interval' }, aggs: { - ownerIds: { - cardinality: { - field: 'task.ownerId', - }, - }, + taskType: { terms: { size: taskTypeTermAggSize, field: 'task.taskType' } }, }, }, + ownerIds: { + filter: { range: { 'task.startedAt': { gte: 'now-1w/w' } } }, + aggs: { ownerIds: { cardinality: { field: 'task.ownerId' } } }, + }, idleTasks: { - filter: { - term: { 'task.status': 'idle' }, - }, + filter: { term: { 'task.status': 'idle' } }, aggs: { scheduleDensity: { // create a window of upcoming tasks @@ -187,7 +190,7 @@ export function createWorkloadAggregator( field: 'task.runAt', fixed_interval: asInterval(pollInterval), }, - // break down each bucket in the historgram by schedule + // break down each bucket in the histogram by schedule aggs: { interval: { terms: { field: 'task.schedule.interval' }, @@ -197,15 +200,10 @@ export function createWorkloadAggregator( }, }, overdue: { - filter: { - range: { - 'task.runAt': { lt: 'now' }, - }, - }, + filter: { range: { 'task.runAt': { lt: 'now' } } }, aggs: { - nonRecurring: { - missing: { field: 'task.schedule' }, - }, + taskTypes: { terms: { size: taskTypeTermAggSize, field: 'task.taskType' } }, + nonRecurring: { missing: { field: 'task.schedule.interval' } }, }, }, }, @@ -226,11 +224,13 @@ export function createWorkloadAggregator( const taskTypes = aggregations.taskType.buckets; const nonRecurring = aggregations.nonRecurringTasks.doc_count; + const nonRecurringTaskTypes = aggregations.nonRecurringTasks.taskType.buckets; const ownerIds = aggregations.ownerIds.ownerIds.value; const { overdue: { doc_count: overdue, + taskTypes: { buckets: taskTypesOverdue = [] } = {}, nonRecurring: { doc_count: overdueNonRecurring }, }, scheduleDensity: { buckets: [scheduleDensity] = [] } = {}, @@ -243,6 +243,7 @@ export function createWorkloadAggregator( asSeconds: parseIntervalAsSecond(schedule.key as string), count: schedule.doc_count, }; + accm.schedules.push(parsedSchedule); if (parsedSchedule.asSeconds <= 60) { accm.cadence.perMinute += @@ -257,11 +258,7 @@ export function createWorkloadAggregator( return accm; }, { - cadence: { - perMinute: 0, - perHour: 0, - perDay: 0, - }, + cadence: { perMinute: 0, perHour: 0, perDay: 0 }, schedules: [] as Array<{ interval: string; asSeconds: number; @@ -270,20 +267,42 @@ export function createWorkloadAggregator( } ); + const totalNonRecurringCost = getTotalCost(nonRecurringTaskTypes, taskDefinitions); + const totalOverdueCost = getTotalCost(taskTypesOverdue, taskDefinitions); + + let totalCost = 0; + const taskTypeSummary = taskTypes.reduce((acc, bucket) => { + const value = bucket as TaskTypeWithStatusBucket; + const taskDef = taskDefinitions.get(value.key as string); + if (taskDef) { + const cost = value.doc_count * taskDef?.cost ?? TaskCost.Normal; + + totalCost += cost; + return Object.assign(acc, { + [value.key as string]: { + count: value.doc_count, + cost, + status: mapValues(keyBy(value.status.buckets, 'key'), 'doc_count'), + }, + }); + } else { + // task type is not registered with dictionary, do not add to summary + return acc; + } + }, {}); + const summary: WorkloadStat = { count, - task_types: mapValues(keyBy(taskTypes, 'key'), ({ doc_count: docCount, status }) => { - return { - count: docCount, - status: mapValues(keyBy(status.buckets, 'key'), 'doc_count'), - }; - }), + cost: totalCost, + task_types: taskTypeSummary, non_recurring: nonRecurring, + non_recurring_cost: totalNonRecurringCost, owner_ids: ownerIdsQueue(ownerIds), schedule: schedules .sort((scheduleLeft, scheduleRight) => scheduleLeft.asSeconds - scheduleRight.asSeconds) .map((schedule) => [schedule.interval, schedule.count]), overdue, + overdue_cost: totalOverdueCost, overdue_non_recurring: overdueNonRecurring, estimated_schedule_density: padBuckets( scheduleDensityBuckets, @@ -457,40 +476,37 @@ export interface WorkloadAggregationResponse { taskType: TaskTypeAggregation; schedule: ScheduleAggregation; idleTasks: IdleTasksAggregation; - nonRecurringTasks: { - doc_count: number; - }; - ownerIds: { - ownerIds: { - value: number; - }; - }; + nonRecurringTasks: { doc_count: number; taskType: TaskTypeAggregation }; + ownerIds: { ownerIds: { value: number } }; [otherAggs: string]: estypes.AggregationsAggregate; } + +export type TaskTypeWithStatusBucket = TaskTypeBucket & { + status: { + buckets: Array<{ + doc_count: number; + key: string | number; + }>; + doc_count_error_upper_bound?: number | undefined; + sum_other_doc_count?: number | undefined; + }; +}; + +export interface TaskTypeBucket { + doc_count: number; + key: string | number; +} + // @ts-expect-error key doesn't accept a string export interface TaskTypeAggregation extends estypes.AggregationsFiltersAggregate { - buckets: Array<{ - doc_count: number; - key: string | number; - status: { - buckets: Array<{ - doc_count: number; - key: string | number; - }>; - doc_count_error_upper_bound?: number | undefined; - sum_other_doc_count?: number | undefined; - }; - }>; + buckets: Array<TaskTypeBucket | TaskTypeWithStatusBucket>; doc_count_error_upper_bound?: number | undefined; sum_other_doc_count?: number | undefined; } // @ts-expect-error key doesn't accept a string export interface ScheduleAggregation extends estypes.AggregationsFiltersAggregate { - buckets: Array<{ - doc_count: number; - key: string | number; - }>; + buckets: Array<{ doc_count: number; key: string | number }>; doc_count_error_upper_bound?: number | undefined; sum_other_doc_count?: number | undefined; } @@ -518,9 +534,8 @@ export interface IdleTasksAggregation extends estypes.AggregationsFiltersAggrega }; overdue: { doc_count: number; - nonRecurring: { - doc_count: number; - }; + nonRecurring: { doc_count: number }; + taskTypes: TaskTypeAggregation; }; } @@ -537,3 +552,16 @@ interface DateRangeBucket { from_as_string?: string; doc_count: number; } + +function getTotalCost(taskTypeBuckets: TaskTypeBucket[], definitions: TaskTypeDictionary): number { + let cost = 0; + for (const bucket of taskTypeBuckets) { + const taskDef = definitions.get(bucket.key as string); + if (taskDef) { + cost += bucket.doc_count * taskDef?.cost ?? TaskCost.Normal; + } else { + // task type is not registered with dictionary, do not add to cost + } + } + return cost; +} diff --git a/x-pack/plugins/task_manager/server/plugin.test.ts b/x-pack/plugins/task_manager/server/plugin.test.ts index 7b80920a57559..353062dfec4fc 100644 --- a/x-pack/plugins/task_manager/server/plugin.test.ts +++ b/x-pack/plugins/task_manager/server/plugin.test.ts @@ -11,6 +11,7 @@ import { TaskManagerConfig } from './config'; import { Subject } from 'rxjs'; import { bufferCount, take } from 'rxjs'; import { CoreStatus, ServiceStatusLevels } from '@kbn/core/server'; +import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; import { taskPollingLifecycleMock } from './polling_lifecycle.mock'; import { TaskPollingLifecycle } from './polling_lifecycle'; import type { TaskPollingLifecycle as TaskPollingLifecycleClass } from './polling_lifecycle'; @@ -38,7 +39,6 @@ jest.mock('./ephemeral_task_lifecycle', () => { const coreStart = coreMock.createStart(); const pluginInitializerContextParams = { - max_workers: 10, max_attempts: 9, poll_interval: 3000, version_conflict_threshold: 80, @@ -148,7 +148,9 @@ describe('TaskManagerPlugin', () => { pluginInitializerContext.node.roles.backgroundTasks = true; const taskManagerPlugin = new TaskManagerPlugin(pluginInitializerContext); taskManagerPlugin.setup(coreMock.createSetup(), { usageCollection: undefined }); - taskManagerPlugin.start(coreStart); + taskManagerPlugin.start(coreStart, { + cloud: cloudMock.createStart(), + }); expect(TaskPollingLifecycle as jest.Mock<TaskPollingLifecycleClass>).toHaveBeenCalledTimes(1); expect( @@ -163,7 +165,9 @@ describe('TaskManagerPlugin', () => { pluginInitializerContext.node.roles.backgroundTasks = false; const taskManagerPlugin = new TaskManagerPlugin(pluginInitializerContext); taskManagerPlugin.setup(coreMock.createSetup(), { usageCollection: undefined }); - taskManagerPlugin.start(coreStart); + taskManagerPlugin.start(coreStart, { + cloud: cloudMock.createStart(), + }); expect(TaskPollingLifecycle as jest.Mock<TaskPollingLifecycleClass>).not.toHaveBeenCalled(); expect( diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 1926b48b31ea6..23ef12605baa2 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -18,6 +18,7 @@ import { ServiceStatusLevels, CoreStatus, } from '@kbn/core/server'; +import type { CloudStart } from '@kbn/cloud-plugin/server'; import { registerDeleteInactiveNodesTaskDefinition, scheduleDeleteInactiveNodesTaskDefinition, @@ -43,6 +44,7 @@ import { setupIntervalLogging } from './lib/log_health_metrics'; import { metricsStream, Metrics } from './metrics'; import { TaskManagerMetricsCollector } from './metrics/task_metrics_collector'; import { TaskPartitioner } from './lib/task_partitioner'; +import { getDefaultCapacity } from './lib/get_default_capacity'; export interface TaskManagerSetupContract { /** @@ -76,6 +78,10 @@ export type TaskManagerStartContract = Pick< getRegisteredTypes: () => string[]; }; +export interface TaskManagerPluginStart { + cloud?: CloudStart; +} + const LogHealthForBackgroundTasksOnlyMinutes = 60; export class TaskManagerPlugin @@ -99,6 +105,7 @@ export class TaskManagerPlugin private taskManagerMetricsCollector?: TaskManagerMetricsCollector; private nodeRoles: PluginInitializerContext['node']['roles']; private kibanaDiscoveryService?: KibanaDiscoveryService; + private heapSizeLimit: number = 0; constructor(private readonly initContext: PluginInitializerContext) { this.initContext = initContext; @@ -122,6 +129,13 @@ export class TaskManagerPlugin ): TaskManagerSetupContract { this.elasticsearchAndSOAvailability$ = getElasticsearchAndSOAvailability(core.status.core$); + core.metrics + .getOpsMetrics$() + .pipe(distinctUntilChanged()) + .subscribe((metrics) => { + this.heapSizeLimit = metrics.process.memory.heap.size_limit; + }); + setupSavedObjects(core.savedObjects, this.config); this.taskManagerId = this.initContext.env.instanceUuid; @@ -232,12 +246,10 @@ export class TaskManagerPlugin }; } - public start({ - savedObjects, - elasticsearch, - executionContext, - docLinks, - }: CoreStart): TaskManagerStartContract { + public start( + { savedObjects, elasticsearch, executionContext, docLinks }: CoreStart, + { cloud }: TaskManagerPluginStart + ): TaskManagerStartContract { const savedObjectsRepository = savedObjects.createInternalRepository([ TASK_SO_NAME, BACKGROUND_TASK_NODE_SO_NAME, @@ -267,11 +279,31 @@ export class TaskManagerPlugin requestTimeouts: this.config.request_timeouts, }); + const isServerless = this.initContext.env.packageInfo.buildFlavor === 'serverless'; + + const defaultCapacity = getDefaultCapacity({ + claimStrategy: this.config?.claim_strategy, + heapSizeLimit: this.heapSizeLimit, + isCloud: cloud?.isCloudEnabled ?? false, + isServerless, + isBackgroundTaskNodeOnly: this.isNodeBackgroundTasksOnly(), + }); + + this.logger.info( + `Task manager isCloud=${ + cloud?.isCloudEnabled ?? false + } isServerless=${isServerless} claimStrategy=${ + this.config!.claim_strategy + } isBackgroundTaskNodeOnly=${this.isNodeBackgroundTasksOnly()} heapSizeLimit=${ + this.heapSizeLimit + } defaultCapacity=${defaultCapacity}` + ); + const managedConfiguration = createManagedConfiguration({ - logger: this.logger, + config: this.config!, errors$: taskStore.errors$, - startingMaxWorkers: this.config!.max_workers, - startingPollInterval: this.config!.poll_interval, + defaultCapacity, + logger: this.logger, }); // Only poll for tasks if configured to run tasks @@ -310,16 +342,17 @@ export class TaskManagerPlugin }); } - createMonitoringStats( + createMonitoringStats({ taskStore, - this.elasticsearchAndSOAvailability$!, - this.config!, - managedConfiguration, - this.logger, - this.adHocTaskCounter, - this.taskPollingLifecycle, - this.ephemeralTaskLifecycle - ).subscribe((stat) => this.monitoringStats$.next(stat)); + elasticsearchAndSOAvailability$: this.elasticsearchAndSOAvailability$!, + config: this.config!, + managedConfig: managedConfiguration, + logger: this.logger, + adHocTaskCounter: this.adHocTaskCounter, + taskDefinitions: this.definitions, + taskPollingLifecycle: this.taskPollingLifecycle, + ephemeralTaskLifecycle: this.ephemeralTaskLifecycle, + }).subscribe((stat) => this.monitoringStats$.next(stat)); metricsStream({ config: this.config!, diff --git a/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts b/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts index f06c43bc15587..11741aeadcf2d 100644 --- a/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts +++ b/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts @@ -22,10 +22,10 @@ describe('delayOnClaimConflicts', () => { 'initializes with a delay of 0', fakeSchedulers(async () => { const pollInterval = 100; - const maxWorkers = 10; + const capacity = 10; const taskLifecycleEvents$ = new Subject<TaskLifecycleEvent>(); const delays = delayOnClaimConflicts( - of(maxWorkers), + of(capacity), of(pollInterval), taskLifecycleEvents$, 80, @@ -42,11 +42,11 @@ describe('delayOnClaimConflicts', () => { 'emits a random delay whenever p50 of claim clashes exceed 80% of available max_workers', fakeSchedulers(async () => { const pollInterval = 100; - const maxWorkers = 10; + const capacity = 10; const taskLifecycleEvents$ = new Subject<TaskLifecycleEvent>(); const delays$ = firstValueFrom<number[]>( - delayOnClaimConflicts(of(maxWorkers), of(pollInterval), taskLifecycleEvents$, 80, 2).pipe( + delayOnClaimConflicts(of(capacity), of(pollInterval), taskLifecycleEvents$, 80, 2).pipe( take(2), bufferCount(2) ) @@ -60,7 +60,6 @@ describe('delayOnClaimConflicts', () => { tasksUpdated: 0, tasksConflicted: 8, tasksClaimed: 0, - tasksRejected: 0, }, docs: [], }) @@ -94,7 +93,6 @@ describe('delayOnClaimConflicts', () => { tasksUpdated: 0, tasksConflicted: 8, tasksClaimed: 0, - tasksRejected: 0, }, docs: [], }) @@ -111,7 +109,6 @@ describe('delayOnClaimConflicts', () => { tasksUpdated: 0, tasksConflicted: 10, tasksClaimed: 0, - tasksRejected: 0, }, docs: [], }) @@ -137,18 +134,14 @@ describe('delayOnClaimConflicts', () => { 'doesnt emit a new delay when conflicts have reduced', fakeSchedulers(async () => { const pollInterval = 100; - const maxWorkers = 10; + const capacity = 10; const taskLifecycleEvents$ = new Subject<TaskLifecycleEvent>(); const handler = jest.fn(); - delayOnClaimConflicts( - of(maxWorkers), - of(pollInterval), - taskLifecycleEvents$, - 80, - 2 - ).subscribe(handler); + delayOnClaimConflicts(of(capacity), of(pollInterval), taskLifecycleEvents$, 80, 2).subscribe( + handler + ); await sleep(0); expect(handler).toHaveBeenCalledWith(0); @@ -161,7 +154,6 @@ describe('delayOnClaimConflicts', () => { tasksUpdated: 0, tasksConflicted: 8, tasksClaimed: 0, - tasksRejected: 0, }, docs: [], }) @@ -182,7 +174,6 @@ describe('delayOnClaimConflicts', () => { tasksUpdated: 0, tasksConflicted: 7, tasksClaimed: 0, - tasksRejected: 0, }, docs: [], }) @@ -201,7 +192,6 @@ describe('delayOnClaimConflicts', () => { tasksUpdated: 0, tasksConflicted: 9, tasksClaimed: 0, - tasksRejected: 0, }, docs: [], }) diff --git a/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.ts b/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.ts index f491d58fc59ee..21b16b1a8d5c5 100644 --- a/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.ts +++ b/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.ts @@ -19,13 +19,14 @@ import { ManagedConfiguration } from '../lib/create_managed_configuration'; import { TaskLifecycleEvent } from '../polling_lifecycle'; import { isTaskPollingCycleEvent } from '../task_events'; import { ClaimAndFillPoolResult } from '../lib/fill_pool'; -import { createRunningAveragedStat } from '../monitoring/task_run_calcultors'; +import { createRunningAveragedStat } from '../monitoring/task_run_calculators'; +import { getCapacityInWorkers } from '../task_pool'; /** * Emits a delay amount in ms to apply to polling whenever the task store exceeds a threshold of claim claimClashes */ export function delayOnClaimConflicts( - maxWorkersConfiguration$: ManagedConfiguration['maxWorkersConfiguration$'], + capacityConfiguration$: ManagedConfiguration['capacityConfiguration$'], pollIntervalConfiguration$: ManagedConfiguration['pollIntervalConfiguration$'], taskLifecycleEvents$: Observable<TaskLifecycleEvent>, claimClashesPercentageThreshold: number, @@ -37,7 +38,7 @@ export function delayOnClaimConflicts( merge( of(0), combineLatest([ - maxWorkersConfiguration$, + capacityConfiguration$, pollIntervalConfiguration$, taskLifecycleEvents$.pipe( map<TaskLifecycleEvent, Option<number>>((taskEvent: TaskLifecycleEvent) => @@ -51,7 +52,10 @@ export function delayOnClaimConflicts( map((claimClashes: Option<number>) => (claimClashes as Some<number>).value) ), ]).pipe( - map(([maxWorkers, pollInterval, latestClaimConflicts]) => { + map(([capacity, pollInterval, latestClaimConflicts]) => { + // convert capacity to maxWorkers + const maxWorkers = getCapacityInWorkers(capacity); + // add latest claimConflict count to queue claimConflictQueue(latestClaimConflicts); diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts index baf45cb65ea1e..e804f1c166cee 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.test.ts @@ -20,6 +20,8 @@ import { asOk, Err, isErr, isOk, Result } from './lib/result_type'; import { FillPoolResult } from './lib/fill_pool'; import { ElasticsearchResponseError } from './lib/identify_es_error'; import { executionContextServiceMock } from '@kbn/core/server/mocks'; +import { TaskCost } from './task'; +import { CLAIM_STRATEGY_MGET } from './config'; import { TaskPartitioner } from './lib/task_partitioner'; import { KibanaDiscoveryService } from './kibana_discovery_service'; @@ -44,7 +46,6 @@ describe('TaskPollingLifecycle', () => { const taskManagerOpts = { config: { enabled: true, - max_workers: 10, index: 'foo', max_attempts: 9, poll_interval: 6000000, @@ -90,7 +91,8 @@ describe('TaskPollingLifecycle', () => { unusedTypes: [], definitions: new TaskTypeDictionary(taskManagerLogger), middleware: createInitialMiddleware(), - maxWorkersConfiguration$: of(100), + startingCapacity: 20, + capacityConfiguration$: of(20), pollIntervalConfiguration$: of(100), executionContext, taskPartitioner: new TaskPartitioner('test', {} as KibanaDiscoveryService), @@ -105,12 +107,23 @@ describe('TaskPollingLifecycle', () => { afterEach(() => clock.restore()); describe('start', () => { + taskManagerOpts.definitions.registerTaskDefinitions({ + report: { + title: 'report', + maxConcurrency: 1, + cost: TaskCost.ExtraLarge, + createTaskRunner: jest.fn(), + }, + quickReport: { + title: 'quickReport', + maxConcurrency: 5, + createTaskRunner: jest.fn(), + }, + }); + test('begins polling once the ES and SavedObjects services are available', () => { const elasticsearchAndSOAvailability$ = new Subject<boolean>(); - new TaskPollingLifecycle({ - ...taskManagerOpts, - elasticsearchAndSOAvailability$, - }); + new TaskPollingLifecycle({ ...taskManagerOpts, elasticsearchAndSOAvailability$ }); clock.tick(150); expect(mockTaskClaiming.claimAvailableTasksIfCapacityIsAvailable).not.toHaveBeenCalled(); @@ -121,56 +134,71 @@ describe('TaskPollingLifecycle', () => { expect(mockTaskClaiming.claimAvailableTasksIfCapacityIsAvailable).toHaveBeenCalled(); }); - test('provides TaskClaiming with the capacity available', () => { + test('provides TaskClaiming with the capacity available when strategy = CLAIM_STRATEGY_DEFAULT', () => { const elasticsearchAndSOAvailability$ = new Subject<boolean>(); - const maxWorkers$ = new Subject<number>(); - taskManagerOpts.definitions.registerTaskDefinitions({ - report: { - title: 'report', - maxConcurrency: 1, - createTaskRunner: jest.fn(), - }, - quickReport: { - title: 'quickReport', - maxConcurrency: 5, - createTaskRunner: jest.fn(), - }, - }); + const capacity$ = new Subject<number>(); new TaskPollingLifecycle({ ...taskManagerOpts, elasticsearchAndSOAvailability$, - maxWorkersConfiguration$: maxWorkers$, + capacityConfiguration$: capacity$, }); const taskClaimingGetCapacity = (TaskClaiming as jest.Mock<TaskClaimingClass>).mock - .calls[0][0].getCapacity; + .calls[0][0].getAvailableCapacity; - maxWorkers$.next(20); - expect(taskClaimingGetCapacity()).toEqual(20); + capacity$.next(40); + expect(taskClaimingGetCapacity()).toEqual(40); expect(taskClaimingGetCapacity('report')).toEqual(1); expect(taskClaimingGetCapacity('quickReport')).toEqual(5); - maxWorkers$.next(30); - expect(taskClaimingGetCapacity()).toEqual(30); + capacity$.next(60); + expect(taskClaimingGetCapacity()).toEqual(60); expect(taskClaimingGetCapacity('report')).toEqual(1); expect(taskClaimingGetCapacity('quickReport')).toEqual(5); - maxWorkers$.next(2); - expect(taskClaimingGetCapacity()).toEqual(2); + capacity$.next(4); + expect(taskClaimingGetCapacity()).toEqual(4); expect(taskClaimingGetCapacity('report')).toEqual(1); - expect(taskClaimingGetCapacity('quickReport')).toEqual(2); + expect(taskClaimingGetCapacity('quickReport')).toEqual(4); }); - }); - describe('stop', () => { - test('stops polling once the ES and SavedObjects services become unavailable', () => { + test('provides TaskClaiming with the capacity available when strategy = CLAIM_STRATEGY_MGET', () => { const elasticsearchAndSOAvailability$ = new Subject<boolean>(); + const capacity$ = new Subject<number>(); + new TaskPollingLifecycle({ - elasticsearchAndSOAvailability$, ...taskManagerOpts, + config: { ...taskManagerOpts.config, claim_strategy: CLAIM_STRATEGY_MGET }, + elasticsearchAndSOAvailability$, + capacityConfiguration$: capacity$, }); + const taskClaimingGetCapacity = (TaskClaiming as jest.Mock<TaskClaimingClass>).mock + .calls[0][0].getAvailableCapacity; + + capacity$.next(40); + expect(taskClaimingGetCapacity()).toEqual(80); + expect(taskClaimingGetCapacity('report')).toEqual(10); + expect(taskClaimingGetCapacity('quickReport')).toEqual(10); + + capacity$.next(60); + expect(taskClaimingGetCapacity()).toEqual(120); + expect(taskClaimingGetCapacity('report')).toEqual(10); + expect(taskClaimingGetCapacity('quickReport')).toEqual(10); + + capacity$.next(4); + expect(taskClaimingGetCapacity()).toEqual(8); + expect(taskClaimingGetCapacity('report')).toEqual(8); + expect(taskClaimingGetCapacity('quickReport')).toEqual(8); + }); + }); + + describe('stop', () => { + test('stops polling once the ES and SavedObjects services become unavailable', () => { + const elasticsearchAndSOAvailability$ = new Subject<boolean>(); + new TaskPollingLifecycle({ elasticsearchAndSOAvailability$, ...taskManagerOpts }); + elasticsearchAndSOAvailability$.next(true); clock.tick(150); @@ -216,7 +244,7 @@ describe('TaskPollingLifecycle', () => { of( asOk({ docs: [], - stats: { tasksUpdated: 0, tasksConflicted: 0, tasksClaimed: 0, tasksRejected: 0 }, + stats: { tasksUpdated: 0, tasksConflicted: 0, tasksClaimed: 0 }, }) ) ); @@ -298,7 +326,47 @@ describe('TaskPollingLifecycle', () => { of( asOk({ docs: [], - stats: { tasksUpdated: 0, tasksConflicted: 0, tasksClaimed: 0, tasksRejected: 0 }, + stats: { tasksUpdated: 0, tasksConflicted: 0, tasksClaimed: 0 }, + }) + ) + ); + const elasticsearchAndSOAvailability$ = new Subject<boolean>(); + const taskPollingLifecycle = new TaskPollingLifecycle({ + ...taskManagerOpts, + elasticsearchAndSOAvailability$, + }); + + const emittedEvents: TaskLifecycleEvent[] = []; + + taskPollingLifecycle.events.subscribe((event: TaskLifecycleEvent) => + emittedEvents.push(event) + ); + + elasticsearchAndSOAvailability$.next(true); + expect(mockTaskClaiming.claimAvailableTasksIfCapacityIsAvailable).toHaveBeenCalled(); + await retryUntil('workerUtilizationEvent emitted', () => { + return !!emittedEvents.find( + (event: TaskLifecycleEvent) => event.id === 'workerUtilization' + ); + }); + + const workerUtilizationEvent = emittedEvents.find( + (event: TaskLifecycleEvent) => event.id === 'workerUtilization' + ); + expect(workerUtilizationEvent).toEqual({ + id: 'workerUtilization', + type: 'TASK_MANAGER_STAT', + event: { tag: 'ok', value: 0 }, + }); + }); + + test('should set utilization to max when capacity is not fully reached but there are tasks left unclaimed', async () => { + clock.restore(); + mockTaskClaiming.claimAvailableTasksIfCapacityIsAvailable.mockImplementation(() => + of( + asOk({ + docs: [], + stats: { tasksUpdated: 0, tasksConflicted: 0, tasksClaimed: 0, tasksLeftUnclaimed: 2 }, }) ) ); @@ -321,6 +389,15 @@ describe('TaskPollingLifecycle', () => { (event: TaskLifecycleEvent) => event.id === 'workerUtilization' ); }); + + const workerUtilizationEvent = emittedEvents.find( + (event: TaskLifecycleEvent) => event.id === 'workerUtilization' + ); + expect(workerUtilizationEvent).toEqual({ + id: 'workerUtilization', + type: 'TASK_MANAGER_STAT', + event: { tag: 'ok', value: 100 }, + }); }); test('should emit event when polling error occurs', async () => { diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.ts index 3b9c5621da0b9..f13a7ad20806c 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.ts @@ -45,6 +45,8 @@ import { TaskClaiming } from './queries/task_claiming'; import { ClaimOwnershipResult } from './task_claimers'; import { TaskPartitioner } from './lib/task_partitioner'; +const MAX_BUFFER_OPERATIONS = 100; + export interface ITaskEventEmitter<T> { get events(): Observable<T>; } @@ -101,7 +103,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven constructor({ logger, middleware, - maxWorkersConfiguration$, + capacityConfiguration$, pollIntervalConfiguration$, // Elasticsearch and SavedObjects availability status elasticsearchAndSOAvailability$, @@ -124,13 +126,15 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven const emitEvent = (event: TaskLifecycleEvent) => this.events$.next(event); this.bufferedStore = new BufferedTaskStore(this.store, { - bufferMaxOperations: config.max_workers, + bufferMaxOperations: MAX_BUFFER_OPERATIONS, logger, }); this.pool = new TaskPool({ logger, - maxWorkers$: maxWorkersConfiguration$, + strategy: config.claim_strategy, + capacity$: capacityConfiguration$, + definitions: this.definitions, }); this.pool.load.subscribe(emitEvent); @@ -142,17 +146,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven definitions, unusedTypes, logger: this.logger, - getCapacity: (taskType?: string) => - taskType && this.definitions.get(taskType)?.maxConcurrency - ? Math.max( - Math.min( - this.pool.availableWorkers, - this.definitions.get(taskType)!.maxConcurrency! - - this.pool.getOccupiedWorkersByType(taskType) - ), - 0 - ) - : this.pool.availableWorkers, + getAvailableCapacity: (taskType?: string) => this.pool.availableCapacity(taskType), taskPartitioner, }); // pipe taskClaiming events into the lifecycle event stream @@ -163,7 +157,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven let pollIntervalDelay$: Observable<number> | undefined; if (claimStrategy === CLAIM_STRATEGY_DEFAULT) { pollIntervalDelay$ = delayOnClaimConflicts( - maxWorkersConfiguration$, + capacityConfiguration$, pollIntervalConfiguration$, this.events$, config.version_conflict_threshold, @@ -177,19 +171,22 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven pollInterval$: pollIntervalConfiguration$, pollIntervalDelay$, getCapacity: () => { - const capacity = this.pool.availableWorkers; + const capacity = this.pool.availableCapacity(); if (!capacity) { + const usedCapacityPercentage = this.pool.usedCapacityPercentage; + // if there isn't capacity, emit a load event so that we can expose how often // high load causes the poller to skip work (work isn't called when there is no capacity) - this.emitEvent(asTaskManagerStatEvent('load', asOk(this.pool.workerLoad))); + this.emitEvent(asTaskManagerStatEvent('load', asOk(usedCapacityPercentage))); // Emit event indicating task manager utilization - this.emitEvent(asTaskManagerStatEvent('workerUtilization', asOk(this.pool.workerLoad))); + this.emitEvent(asTaskManagerStatEvent('workerUtilization', asOk(usedCapacityPercentage))); } return capacity; }, work: this.pollForWork, }); + this.subscribeToPoller(poller.events$); elasticsearchAndSOAvailability$.subscribe((areESAndSOAvailable) => { @@ -262,7 +259,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven const [result] = await Promise.all([this.pool.run(tasksToRun), ...removeTaskPromises]); // Emit the load after fetching tasks, giving us a good metric for evaluating how // busy Task manager tends to be in this Kibana instance - this.emitEvent(asTaskManagerStatEvent('load', asOk(this.pool.workerLoad))); + this.emitEvent(asTaskManagerStatEvent('load', asOk(this.pool.usedCapacityPercentage))); return result; } ); @@ -285,16 +282,29 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven // Emit event indicating task manager utilization % at the end of a polling cycle // Because there was a polling error, no tasks were claimed so this represents the number of workers busy - this.emitEvent(asTaskManagerStatEvent('workerUtilization', asOk(this.pool.workerLoad))); + this.emitEvent( + asTaskManagerStatEvent('workerUtilization', asOk(this.pool.usedCapacityPercentage)) + ); }) ) ) .pipe( tap( - mapOk(() => { + mapOk((results: TimedFillPoolResult) => { // Emit event indicating task manager utilization % at the end of a polling cycle - // This represents the number of workers busy + number of tasks claimed in this cycle - this.emitEvent(asTaskManagerStatEvent('workerUtilization', asOk(this.pool.workerLoad))); + + // Get the actual utilization as a percentage + let tmUtilization = this.pool.usedCapacityPercentage; + + // Check whether there are any tasks left unclaimed + // If we're not at capacity and there are unclaimed tasks, then + // there must be high cost tasks that need to be claimed + // Artificially inflate the utilization to represent the unclaimed load + if (tmUtilization < 100 && (results.stats?.tasksLeftUnclaimed ?? 0) > 0) { + tmUtilization = 100; + } + + this.emitEvent(asTaskManagerStatEvent('workerUtilization', asOk(tmUtilization))); }) ) ) diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts index bc4adb71dd4a1..de57a73f80533 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.test.ts @@ -80,7 +80,7 @@ describe('TaskClaiming', () => { unusedTypes: [], taskStore: taskStoreMock.create({ taskManagerId: '' }), maxAttempts: 2, - getCapacity: () => 10, + getAvailableCapacity: () => 10, taskPartitioner, }); @@ -130,7 +130,7 @@ describe('TaskClaiming', () => { unusedTypes: [], taskStore: taskStoreMock.create({ taskManagerId: '' }), maxAttempts: 2, - getCapacity: () => 10, + getAvailableCapacity: () => 10, taskPartitioner, }); diff --git a/x-pack/plugins/task_manager/server/queries/task_claiming.ts b/x-pack/plugins/task_manager/server/queries/task_claiming.ts index 188f47b0d2d2f..f5ef18452509b 100644 --- a/x-pack/plugins/task_manager/server/queries/task_claiming.ts +++ b/x-pack/plugins/task_manager/server/queries/task_claiming.ts @@ -38,7 +38,7 @@ export interface TaskClaimingOpts { taskStore: TaskStore; maxAttempts: number; excludedTaskTypes: string[]; - getCapacity: (taskType?: string) => number; + getAvailableCapacity: (taskType?: string) => number; taskPartitioner: TaskPartitioner; } @@ -87,7 +87,7 @@ export class TaskClaiming { private definitions: TaskTypeDictionary; private events$: Subject<TaskClaim>; private taskStore: TaskStore; - private getCapacity: (taskType?: string) => number; + private getAvailableCapacity: (taskType?: string) => number; private logger: Logger; private readonly taskClaimingBatchesByType: TaskClaimingBatches; private readonly taskMaxAttempts: Record<string, number>; @@ -106,7 +106,7 @@ export class TaskClaiming { this.definitions = opts.definitions; this.maxAttempts = opts.maxAttempts; this.taskStore = opts.taskStore; - this.getCapacity = opts.getCapacity; + this.getAvailableCapacity = opts.getAvailableCapacity; this.logger = opts.logger.get('taskClaiming'); this.taskClaimingBatchesByType = this.partitionIntoClaimingBatches(this.definitions); this.taskMaxAttempts = Object.fromEntries(this.normalizeMaxAttempts(this.definitions)); @@ -170,13 +170,13 @@ export class TaskClaiming { public claimAvailableTasksIfCapacityIsAvailable( claimingOptions: Omit<OwnershipClaimingOpts, 'size' | 'taskTypes'> ): Observable<Result<ClaimOwnershipResult, FillPoolResult>> { - if (this.getCapacity()) { + if (this.getAvailableCapacity()) { const opts: TaskClaimerOpts = { batches: this.getClaimingBatches(), claimOwnershipUntil: claimingOptions.claimOwnershipUntil, taskStore: this.taskStore, events$: this.events$, - getCapacity: this.getCapacity, + getCapacity: this.getAvailableCapacity, unusedTypes: this.unusedTypes, definitions: this.definitions, taskMaxAttempts: this.taskMaxAttempts, diff --git a/x-pack/plugins/task_manager/server/routes/health.test.ts b/x-pack/plugins/task_manager/server/routes/health.test.ts index a97d99079bc58..9c08c5b5fb4c4 100644 --- a/x-pack/plugins/task_manager/server/routes/health.test.ts +++ b/x-pack/plugins/task_manager/server/routes/health.test.ts @@ -823,7 +823,8 @@ function mockHealthStats(overrides = {}) { configuration: { timestamp: new Date().toISOString(), value: { - max_workers: 10, + capacity: { config: 10, as_cost: 20, as_workers: 10 }, + claim_strategy: 'default', poll_interval: 3000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -841,16 +842,19 @@ function mockHealthStats(overrides = {}) { timestamp: new Date().toISOString(), value: { count: 4, + cost: 8, task_types: { - actions_telemetry: { count: 2, status: { idle: 2 } }, - alerting_telemetry: { count: 1, status: { idle: 1 } }, - session_cleanup: { count: 1, status: { idle: 1 } }, + actions_telemetry: { count: 2, cost: 4, status: { idle: 2 } }, + alerting_telemetry: { count: 1, cost: 2, status: { idle: 1 } }, + session_cleanup: { count: 1, cost: 2, status: { idle: 1 } }, }, schedule: [], overdue: 0, + overdue_cost: 2, overdue_non_recurring: 0, estimatedScheduleDensity: [], non_recurring: 20, + non_recurring_cost: 40, owner_ids: [0, 0, 0, 1, 2, 0, 0, 2, 2, 2, 1, 2, 1, 1], estimated_schedule_density: [], capacity_requirements: { diff --git a/x-pack/plugins/task_manager/server/task.ts b/x-pack/plugins/task_manager/server/task.ts index 8a47424ce4965..772d2615ce84a 100644 --- a/x-pack/plugins/task_manager/server/task.ts +++ b/x-pack/plugins/task_manager/server/task.ts @@ -18,6 +18,12 @@ export enum TaskPriority { Normal = 50, } +export enum TaskCost { + Tiny = 1, + Normal = 2, + ExtraLarge = 10, +} + /* * Type definitions and validations for tasks. */ @@ -129,6 +135,10 @@ export const taskDefinitionSchema = schema.object( * Priority of this task type. Defaults to "NORMAL" if not defined */ priority: schema.maybe(schema.number()), + /** + * Cost to run this task type. Defaults to "Normal". + */ + cost: schema.number({ defaultValue: TaskCost.Normal }), /** * An optional more detailed description of what this task does. */ @@ -174,7 +184,7 @@ export const taskDefinitionSchema = schema.object( paramsSchema: schema.maybe(schema.any()), }, { - validate({ timeout, priority }) { + validate({ timeout, priority, cost }) { if (!isInterval(timeout) || isErr(tryAsResult(() => parseIntervalAsMillisecond(timeout)))) { return `Invalid timeout "${timeout}". Timeout must be of the form "{number}{cadance}" where number is an integer. Example: 5m.`; } @@ -184,6 +194,12 @@ export const taskDefinitionSchema = schema.object( .filter((key) => isNaN(Number(key))) .map((key) => `${key} => ${TaskPriority[key as keyof typeof TaskPriority]}`)}`; } + + if (cost && (!isNumber(cost) || !(cost in TaskCost))) { + return `Invalid cost "${cost}". Cost must be one of ${Object.keys(TaskCost) + .filter((key) => isNaN(Number(key))) + .map((key) => `${key} => ${TaskCost[key as keyof typeof TaskCost]}`)}`; + } }, } ); diff --git a/x-pack/plugins/task_manager/server/task_claimers/index.ts b/x-pack/plugins/task_manager/server/task_claimers/index.ts index 1caa6e2addb0f..134c72041f96f 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/index.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/index.ts @@ -37,6 +37,7 @@ export interface ClaimOwnershipResult { tasksUpdated: number; tasksConflicted: number; tasksClaimed: number; + tasksLeftUnclaimed?: number; }; docs: ConcreteTaskInstance[]; timing?: TaskTiming; @@ -61,13 +62,12 @@ export function getTaskClaimer(logger: Logger, strategy: string): TaskClaimerFn return claimAvailableTasksDefault; } -export function getEmptyClaimOwnershipResult() { +export function getEmptyClaimOwnershipResult(): ClaimOwnershipResult { return { stats: { tasksUpdated: 0, tasksConflicted: 0, tasksClaimed: 0, - tasksRejected: 0, }, docs: [], }; diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_default.test.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_default.test.ts index 8aa206bbe1872..d58fd83486efa 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_default.test.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_default.test.ts @@ -133,7 +133,7 @@ describe('TaskClaiming', () => { excludedTaskTypes, unusedTypes: unusedTaskTypes, maxAttempts: taskClaimingOpts.maxAttempts ?? 2, - getCapacity: taskClaimingOpts.getCapacity ?? (() => 10), + getAvailableCapacity: taskClaimingOpts.getAvailableCapacity ?? (() => 10), taskPartitioner, ...taskClaimingOpts, }); @@ -158,7 +158,7 @@ describe('TaskClaiming', () => { excludedTaskTypes?: string[]; unusedTaskTypes?: string[]; }) { - const getCapacity = taskClaimingOpts.getCapacity ?? (() => 10); + const getCapacity = taskClaimingOpts.getAvailableCapacity ?? (() => 10); const { taskClaiming, store } = initialiseTestClaiming({ storeOpts, taskClaimingOpts, @@ -447,7 +447,7 @@ if (doc['task.runAt'].size()!=0) { }, taskClaimingOpts: { maxAttempts, - getCapacity: (type) => { + getAvailableCapacity: (type) => { switch (type) { case 'limitedToOne': case 'anotherLimitedToOne': @@ -577,7 +577,7 @@ if (doc['task.runAt'].size()!=0) { }, taskClaimingOpts: { maxAttempts, - getCapacity: (type) => { + getAvailableCapacity: (type) => { switch (type) { case 'limitedToTwo': return 2; @@ -686,7 +686,7 @@ if (doc['task.runAt'].size()!=0) { }, taskClaimingOpts: { maxAttempts, - getCapacity: (type) => { + getAvailableCapacity: (type) => { switch (type) { case 'limitedToOne': case 'anotherLimitedToOne': @@ -1139,7 +1139,7 @@ if (doc['task.runAt'].size()!=0) { storeOpts: { taskManagerId, }, - taskClaimingOpts: { getCapacity: () => maxDocs }, + taskClaimingOpts: { getAvailableCapacity: () => maxDocs }, claimingOpts: { claimOwnershipUntil, }, @@ -1219,9 +1219,9 @@ if (doc['task.runAt'].size()!=0) { function instantiateStoreWithMockedApiResponses({ taskManagerId = uuidv4(), definitions = taskDefinitions, - getCapacity = () => 10, + getAvailableCapacity = () => 10, tasksClaimed, - }: Partial<Pick<TaskClaimingOpts, 'definitions' | 'getCapacity'>> & { + }: Partial<Pick<TaskClaimingOpts, 'definitions' | 'getAvailableCapacity'>> & { taskManagerId?: string; tasksClaimed?: ConcreteTaskInstance[][]; } = {}) { @@ -1254,7 +1254,7 @@ if (doc['task.runAt'].size()!=0) { unusedTypes: [], taskStore, maxAttempts: 2, - getCapacity, + getAvailableCapacity, taskPartitioner, }); diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts index f06431229c0b2..e9df1bda7b81d 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.test.ts @@ -15,10 +15,11 @@ import { ConcreteTaskInstance, ConcreteTaskInstanceVersion, TaskPriority, + TaskCost, } from '../task'; import { SearchOpts, StoreOpts } from '../task_store'; import { asTaskClaimEvent, TaskEvent } from '../task_events'; -import { asOk, isOk, unwrap } from '../lib/result_type'; +import { asOk, asErr, isOk, unwrap } from '../lib/result_type'; import { TaskTypeDictionary } from '../task_type_dictionary'; import { mockLogger } from '../test_utils'; import { @@ -33,6 +34,7 @@ import apm from 'elastic-apm-node'; import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; import { ClaimOwnershipResult } from '.'; import { FillPoolResult } from '../lib/fill_pool'; +import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { TaskPartitioner } from '../lib/task_partitioner'; import type { MustNotCondition } from '../queries/query_clauses'; import { @@ -48,6 +50,7 @@ jest.mock('../constants', () => ({ 'anotherLimitedToOne', 'limitedToTwo', 'limitedToFive', + 'yawn', ], })); @@ -70,14 +73,18 @@ const taskDefinitions = new TaskTypeDictionary(taskManagerLogger); taskDefinitions.registerTaskDefinitions({ report: { title: 'report', + cost: TaskCost.Normal, createTaskRunner: jest.fn(), }, dernstraight: { title: 'dernstraight', + cost: TaskCost.ExtraLarge, createTaskRunner: jest.fn(), }, yawn: { title: 'yawn', + cost: TaskCost.Tiny, + maxConcurrency: 1, createTaskRunner: jest.fn(), }, }); @@ -107,6 +114,17 @@ describe('TaskClaiming', () => { }); describe('claimAvailableTasks', () => { + function getVersionMapsFromTasks(tasks: ConcreteTaskInstance[]) { + const versionMap = new Map<string, ConcreteTaskInstanceVersion>(); + const docLatestVersions = new Map<string, ConcreteTaskInstanceVersion>(); + for (const task of tasks) { + versionMap.set(task.id, { esId: task.id, seqNo: 32, primaryTerm: 32 }); + docLatestVersions.set(`task:${task.id}`, { esId: task.id, seqNo: 32, primaryTerm: 32 }); + } + + return { versionMap, docLatestVersions }; + } + function initialiseTestClaiming({ storeOpts = {}, taskClaimingOpts = {}, @@ -127,20 +145,27 @@ describe('TaskClaiming', () => { store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); if (hits == null) hits = [generateFakeTasks(1)]; + + const docVersion = []; if (versionMaps == null) { - versionMaps = [new Map<string, ConcreteTaskInstanceVersion>()]; + versionMaps = []; for (const oneHit of hits) { const map = new Map<string, ConcreteTaskInstanceVersion>(); - versionMaps.push(map); + const mapWithTaskPrefix = new Map<string, ConcreteTaskInstanceVersion>(); for (const task of oneHit) { map.set(task.id, { esId: task.id, seqNo: 32, primaryTerm: 32 }); + mapWithTaskPrefix.set(`task:${task.id}`, { esId: task.id, seqNo: 32, primaryTerm: 32 }); } + versionMaps.push(map); + docVersion.push(mapWithTaskPrefix); } } for (let i = 0; i < hits.length; i++) { store.fetch.mockResolvedValueOnce({ docs: hits[i], versionMap: versionMaps[i] }); - store.getDocVersions.mockResolvedValueOnce(versionMaps[i]); + store.getDocVersions.mockResolvedValueOnce(docVersion[i]); + const oneBulkGetResult = hits[i].map((hit) => asOk(hit)); + store.bulkGet.mockResolvedValueOnce(oneBulkGetResult); const oneBulkResult = hits[i].map((hit) => asOk(hit)); store.bulkUpdate.mockResolvedValueOnce(oneBulkResult); } @@ -153,7 +178,7 @@ describe('TaskClaiming', () => { excludedTaskTypes, unusedTypes: unusedTaskTypes, maxAttempts: taskClaimingOpts.maxAttempts ?? 2, - getCapacity: taskClaimingOpts.getCapacity ?? (() => 10), + getAvailableCapacity: taskClaimingOpts.getAvailableCapacity ?? (() => 10), taskPartitioner, ...taskClaimingOpts, }); @@ -200,6 +225,14 @@ describe('TaskClaiming', () => { return unwrap(resultOrErr) as ClaimOwnershipResult; }); + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(store.fetch.mock.calls).toMatchObject({}); + expect(store.getDocVersions.mock.calls).toMatchObject({}); return results.map((result, index) => ({ result, args: { @@ -286,8 +319,1250 @@ describe('TaskClaiming', () => { expect(result).toMatchObject({}); }); + test('should limit claimed tasks based on task cost and available capacity', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), // total cost = 2 + mockInstance({ id: `id-2`, taskType: 'report' }), // total cost = 4 + mockInstance({ id: `id-3`, taskType: 'yawn' }), // total cost = 5 + mockInstance({ id: `id-4`, taskType: 'dernstraight' }), // claiming this will exceed the available capacity + mockInstance({ id: `id-5`, taskType: 'report' }), + mockInstance({ id: `id-6`, taskType: 'report' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce( + [fetchedTasks[0], fetchedTasks[1], fetchedTasks[2]].map(asOk) + ); + store.bulkUpdate.mockResolvedValueOnce( + [fetchedTasks[0], fetchedTasks[1], fetchedTasks[2]].map(asOk) + ); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 3; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith([ + 'task:id-1', + 'task:id-2', + 'task:id-3', + 'task:id-4', + 'task:id-5', + 'task:id-6', + ]); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[0], + ownerId: 'test-test', + retryAt: fetchedTasks[0].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-1', 'id-2', 'id-3']); + + expect(result.stats).toEqual({ + tasksClaimed: 3, + tasksConflicted: 0, + tasksUpdated: 3, + tasksLeftUnclaimed: 3, + }); + expect(result.docs.length).toEqual(3); + }); + + test('should not claim tasks of removed type', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([fetchedTasks[0], fetchedTasks[1]].map(asOk)); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: ['report'], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 1; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; removed: 2;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); + expect(store.bulkUpdate).toHaveBeenCalledTimes(2); + expect(store.bulkUpdate).toHaveBeenNthCalledWith( + 1, + [ + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkUpdate).toHaveBeenNthCalledWith( + 2, + [ + { + ...fetchedTasks[0], + status: 'unrecognized', + }, + { + ...fetchedTasks[1], + status: 'unrecognized', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-3']); + + expect(result.stats).toEqual({ + tasksClaimed: 1, + tasksConflicted: 0, + tasksUpdated: 1, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(1); + }); + + test('should log warning if error updating single removed task as unrecognized', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([ + asOk(fetchedTasks[0]), + // @ts-expect-error + asErr({ + type: 'task', + id: fetchedTasks[1].id, + error: SavedObjectsErrorHelpers.createBadRequestError(), + }), + ]); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: ['report'], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.warn).toHaveBeenCalledWith( + 'Error updating task id-2:task to mark as unrecognized during claim: Bad Request', + { tags: ['claimAvailableTasksMget'] } + ); + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 1; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; removed: 1;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); + expect(store.bulkUpdate).toHaveBeenCalledTimes(2); + expect(store.bulkUpdate).toHaveBeenNthCalledWith( + 1, + [ + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkUpdate).toHaveBeenNthCalledWith( + 2, + [ + { + ...fetchedTasks[0], + status: 'unrecognized', + }, + { + ...fetchedTasks[1], + status: 'unrecognized', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-3']); + + expect(result.stats).toEqual({ + tasksClaimed: 1, + tasksConflicted: 0, + tasksUpdated: 1, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(1); + }); + + test('should log warning if error updating all removed tasks as unrecognized', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockRejectedValueOnce(new Error('Oh no')); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: ['report'], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.warn).toHaveBeenCalledWith( + 'Error updating tasks to mark as unrecognized during claim: Error: Oh no', + { tags: ['claimAvailableTasksMget'] } + ); + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 1; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); + expect(store.bulkGet).toHaveBeenCalledWith(['id-3']); + expect(store.bulkUpdate).toHaveBeenCalledTimes(2); + expect(store.bulkUpdate).toHaveBeenNthCalledWith( + 1, + [ + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkUpdate).toHaveBeenNthCalledWith( + 2, + [ + { + ...fetchedTasks[0], + status: 'unrecognized', + }, + { + ...fetchedTasks[1], + status: 'unrecognized', + }, + ], + { validate: false, excludeLargeFields: true } + ); + + expect(result.stats).toEqual({ + tasksClaimed: 1, + tasksConflicted: 0, + tasksUpdated: 1, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(1); + }); + + test('should handle no tasks to claim', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks: ConcreteTaskInstance[] = []; + + const { versionMap } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).not.toHaveBeenCalled(); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).not.toHaveBeenCalled(); + expect(store.bulkGet).not.toHaveBeenCalled(); + expect(store.bulkUpdate).not.toHaveBeenCalled(); + + expect(result.stats).toEqual({ + tasksClaimed: 0, + tasksConflicted: 0, + tasksUpdated: 0, + }); + expect(result.docs.length).toEqual(0); + }); + + test('should handle tasks with no search version', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + versionMap.delete('id-1'); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce([fetchedTasks[1], fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([fetchedTasks[1], fetchedTasks[2]].map(asOk)); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 2; stale: 0; conflicts: 0; missing: 1; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-2', 'id-3']); + + expect(result.stats).toEqual({ + tasksClaimed: 2, + tasksConflicted: 0, + tasksUpdated: 2, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(2); + }); + + test('should handle tasks with no latest version', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + docLatestVersions.delete('task:id-1'); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce([fetchedTasks[1], fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([fetchedTasks[1], fetchedTasks[2]].map(asOk)); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 2; stale: 0; conflicts: 0; missing: 1; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-2', 'id-3']); + + expect(result.stats).toEqual({ + tasksClaimed: 2, + tasksConflicted: 0, + tasksUpdated: 2, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(2); + }); + + test('should handle stale tasks', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + docLatestVersions.set('task:id-1', { esId: 'task:id-1', seqNo: 33, primaryTerm: 33 }); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce([fetchedTasks[1], fetchedTasks[2]].map(asOk)); + store.bulkUpdate.mockResolvedValueOnce([fetchedTasks[1], fetchedTasks[2]].map(asOk)); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 2; stale: 1; conflicts: 1; missing: 0; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith(['task:id-1', 'task:id-2', 'task:id-3']); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-2', 'id-3']); + + expect(result.stats).toEqual({ + tasksClaimed: 2, + tasksConflicted: 1, + tasksUpdated: 2, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(2); + }); + + test('should correctly handle limited concurrency tasks', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + mockInstance({ id: `id-4`, taskType: 'yawn' }), + mockInstance({ id: `id-5`, taskType: 'report' }), + mockInstance({ id: `id-6`, taskType: 'yawn' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + + store.bulkGet.mockResolvedValueOnce( + [fetchedTasks[0], fetchedTasks[1], fetchedTasks[2], fetchedTasks[4]].map(asOk) + ); + store.bulkUpdate.mockResolvedValueOnce( + [fetchedTasks[0], fetchedTasks[1], fetchedTasks[2], fetchedTasks[4]].map(asOk) + ); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 4; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith([ + 'task:id-1', + 'task:id-2', + 'task:id-3', + 'task:id-4', + 'task:id-5', + 'task:id-6', + ]); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[0], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[4], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-1', 'id-2', 'id-3', 'id-5']); + + expect(result.stats).toEqual({ + tasksClaimed: 4, + tasksConflicted: 0, + tasksUpdated: 4, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(4); + }); + + test('should handle individual errors when bulk getting the full task doc', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + mockInstance({ id: `id-4`, taskType: 'report' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + store.bulkUpdate.mockResolvedValueOnce( + [fetchedTasks[0], fetchedTasks[1], fetchedTasks[2], fetchedTasks[3]].map(asOk) + ); + store.bulkGet.mockResolvedValueOnce([ + asOk(fetchedTasks[0]), + // @ts-expect-error + asErr({ + type: 'task', + id: fetchedTasks[1].id, + error: new Error('Oh no'), + }), + asOk(fetchedTasks[2]), + asOk(fetchedTasks[3]), + ]); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + expect(taskManagerLogger.warn).toHaveBeenCalledWith( + 'Error getting full task id-2:task during claim: Oh no', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith([ + 'task:id-1', + 'task:id-2', + 'task:id-3', + 'task:id-4', + ]); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[0], + ownerId: 'test-test', + retryAt: fetchedTasks[0].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[3], + ownerId: 'test-test', + retryAt: fetchedTasks[3].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-1', 'id-2', 'id-3', 'id-4']); + + expect(result.stats).toEqual({ + tasksClaimed: 3, + tasksConflicted: 0, + tasksUpdated: 3, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(3); + }); + + test('should handle error when bulk getting all full task docs', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + mockInstance({ id: `id-4`, taskType: 'report' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + store.bulkUpdate.mockResolvedValueOnce( + [fetchedTasks[0], fetchedTasks[1], fetchedTasks[2], fetchedTasks[3]].map(asOk) + ); + store.bulkGet.mockRejectedValueOnce(new Error('oh no')); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 0; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + expect(taskManagerLogger.warn).toHaveBeenCalledWith( + 'Error getting full task documents during claim: Error: oh no', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith([ + 'task:id-1', + 'task:id-2', + 'task:id-3', + 'task:id-4', + ]); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[0], + ownerId: 'test-test', + retryAt: fetchedTasks[0].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[3], + ownerId: 'test-test', + retryAt: fetchedTasks[3].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-1', 'id-2', 'id-3', 'id-4']); + + expect(result.stats).toEqual({ + tasksClaimed: 0, + tasksConflicted: 0, + tasksUpdated: 0, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(0); + }); + + test('should handle individual errors when bulk updating the task doc', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + mockInstance({ id: `id-4`, taskType: 'report' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + store.bulkUpdate.mockResolvedValueOnce([ + asOk(fetchedTasks[0]), + // @ts-expect-error + asErr({ + type: 'task', + id: fetchedTasks[1].id, + error: new Error('Oh no'), + }), + asOk(fetchedTasks[2]), + asOk(fetchedTasks[3]), + ]); + store.bulkGet.mockResolvedValueOnce([ + asOk(fetchedTasks[0]), + asOk(fetchedTasks[2]), + asOk(fetchedTasks[3]), + ]); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 3; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 1; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + expect(taskManagerLogger.warn).toHaveBeenCalledWith( + 'Error updating task id-2:task during claim: Oh no', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith([ + 'task:id-1', + 'task:id-2', + 'task:id-3', + 'task:id-4', + ]); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[0], + ownerId: 'test-test', + retryAt: fetchedTasks[0].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[3], + ownerId: 'test-test', + retryAt: fetchedTasks[3].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith(['id-1', 'id-3', 'id-4']); + + expect(result.stats).toEqual({ + tasksClaimed: 3, + tasksConflicted: 0, + tasksUpdated: 3, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(3); + }); + + test('should handle error when bulk updating all task docs', async () => { + const store = taskStoreMock.create({ taskManagerId: 'test-test' }); + store.convertToSavedObjectIds.mockImplementation((ids) => ids.map((id) => `task:${id}`)); + + const fetchedTasks = [ + mockInstance({ id: `id-1`, taskType: 'report' }), + mockInstance({ id: `id-2`, taskType: 'report' }), + mockInstance({ id: `id-3`, taskType: 'yawn' }), + mockInstance({ id: `id-4`, taskType: 'report' }), + ]; + + const { versionMap, docLatestVersions } = getVersionMapsFromTasks(fetchedTasks); + store.fetch.mockResolvedValueOnce({ docs: fetchedTasks, versionMap }); + store.getDocVersions.mockResolvedValueOnce(docLatestVersions); + store.bulkUpdate.mockRejectedValueOnce(new Error('oh no')); + store.bulkGet.mockResolvedValueOnce([]); + + const taskClaiming = new TaskClaiming({ + logger: taskManagerLogger, + strategy: CLAIM_STRATEGY_MGET, + definitions: taskDefinitions, + taskStore: store, + excludedTaskTypes: [], + unusedTypes: [], + maxAttempts: 2, + getAvailableCapacity: () => 10, + taskPartitioner, + }); + + const [resultOrErr] = await getAllAsPromise( + taskClaiming.claimAvailableTasksIfCapacityIsAvailable({ claimOwnershipUntil: new Date() }) + ); + + if (!isOk<ClaimOwnershipResult, FillPoolResult>(resultOrErr)) { + expect(resultOrErr).toBe(undefined); + } + + const result = unwrap(resultOrErr) as ClaimOwnershipResult; + + expect(apm.startTransaction).toHaveBeenCalledWith( + TASK_MANAGER_MARK_AS_CLAIMED, + TASK_MANAGER_TRANSACTION_TYPE + ); + expect(mockApmTrans.end).toHaveBeenCalledWith('success'); + + expect(taskManagerLogger.debug).toHaveBeenCalledWith( + 'task claimer claimed: 0; stale: 0; conflicts: 0; missing: 0; capacity reached: 0; updateErrors: 0; removed: 0;', + { tags: ['claimAvailableTasksMget'] } + ); + expect(taskManagerLogger.warn).toHaveBeenCalledWith( + 'Error updating tasks during claim: Error: oh no', + { tags: ['claimAvailableTasksMget'] } + ); + + expect(store.fetch.mock.calls[0][0]).toMatchObject({ size: 40, seq_no_primary_term: true }); + expect(store.getDocVersions).toHaveBeenCalledWith([ + 'task:id-1', + 'task:id-2', + 'task:id-3', + 'task:id-4', + ]); + expect(store.bulkUpdate).toHaveBeenCalledTimes(1); + expect(store.bulkUpdate).toHaveBeenCalledWith( + [ + { + ...fetchedTasks[0], + ownerId: 'test-test', + retryAt: fetchedTasks[0].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[1], + ownerId: 'test-test', + retryAt: fetchedTasks[1].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[2], + ownerId: 'test-test', + retryAt: fetchedTasks[2].runAt, + status: 'claiming', + }, + { + ...fetchedTasks[3], + ownerId: 'test-test', + retryAt: fetchedTasks[3].runAt, + status: 'claiming', + }, + ], + { validate: false, excludeLargeFields: true } + ); + expect(store.bulkGet).toHaveBeenCalledWith([]); + + expect(result.stats).toEqual({ + tasksClaimed: 0, + tasksConflicted: 0, + tasksUpdated: 0, + tasksLeftUnclaimed: 0, + }); + expect(result.docs.length).toEqual(0); + }); + test('it should filter for specific partitions and tasks without partitions', async () => { const taskManagerId = uuidv4(); + const definitions = new TaskTypeDictionary(mockLogger()); + definitions.registerTaskDefinitions({ + foo: { + title: 'foo', + createTaskRunner: jest.fn(), + }, + bar: { + title: 'bar', + createTaskRunner: jest.fn(), + }, + }); const [ { args: { @@ -297,6 +1572,7 @@ describe('TaskClaiming', () => { ] = await testClaimAvailableTasks({ storeOpts: { taskManagerId, + definitions, }, taskClaimingOpts: {}, claimingOpts: { @@ -352,9 +1628,8 @@ describe('TaskClaiming', () => { Object { "terms": Object { "task.taskType": Array [ - "report", - "dernstraight", - "yawn", + "foo", + "bar", ], }, }, @@ -495,9 +1770,9 @@ describe('TaskClaiming', () => { function instantiateStoreWithMockedApiResponses({ taskManagerId = uuidv4(), definitions = taskDefinitions, - getCapacity = () => 10, + getAvailableCapacity = () => 10, tasksClaimed, - }: Partial<Pick<TaskClaimingOpts, 'definitions' | 'getCapacity'>> & { + }: Partial<Pick<TaskClaimingOpts, 'definitions' | 'getAvailableCapacity'>> & { taskManagerId?: string; tasksClaimed?: ConcreteTaskInstance[][]; } = {}) { @@ -530,7 +1805,7 @@ describe('TaskClaiming', () => { unusedTypes: [], taskStore, maxAttempts: 2, - getCapacity, + getAvailableCapacity, taskPartitioner, }); diff --git a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts index 362c38166339f..7962fdd2b6f8a 100644 --- a/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts +++ b/x-pack/plugins/task_manager/server/task_claimers/strategy_mget.ts @@ -7,9 +7,11 @@ // Basic operation of this task claimer: // - search for candidate tasks to run, more than we actually can run +// - initial search returns a slimmer task document for I/O efficiency (no params or state) // - for each task found, do an mget to get the current seq_no and primary_term // - if the mget result doesn't match the search result, the task is stale -// - from the non-stale search results, return as many as we can run +// - from the non-stale search results, return as many as we can run based on available +// capacity and the cost of each task type to run import { SavedObjectsErrorHelpers } from '@kbn/core/server'; @@ -18,7 +20,7 @@ import { Subject, Observable } from 'rxjs'; import { TaskTypeDictionary } from '../task_type_dictionary'; import { TaskClaimerOpts, ClaimOwnershipResult, getEmptyClaimOwnershipResult } from '.'; -import { ConcreteTaskInstance, TaskStatus, ConcreteTaskInstanceVersion } from '../task'; +import { ConcreteTaskInstance, TaskStatus, ConcreteTaskInstanceVersion, TaskCost } from '../task'; import { TASK_MANAGER_TRANSACTION_TYPE } from '../task_running'; import { isLimited, @@ -112,7 +114,10 @@ async function claimAvailableTasks(opts: TaskClaimerOpts): Promise<ClaimOwnershi taskStore, events$, claimOwnershipUntil, - size: initialCapacity * SIZE_MULTIPLIER_FOR_TASK_FETCH, + // set size to accommodate the possibility of retrieving all + // tasks with the smallest cost, with a size multipler to account + // for possible conflicts + size: initialCapacity * TaskCost.Tiny * SIZE_MULTIPLIER_FOR_TASK_FETCH, taskMaxAttempts, taskPartitioner, }); @@ -156,35 +161,54 @@ async function claimAvailableTasks(opts: TaskClaimerOpts): Promise<ClaimOwnershi continue; } } + // apply limited concurrency limits (TODO: can currently starve other tasks) const candidateTasks = applyLimitedConcurrency(currentTasks, batches); - // build the updated task objects we'll claim - const taskUpdates: ConcreteTaskInstance[] = Array.from(candidateTasks) - .slice(0, initialCapacity) - .map((task) => { - if (task.retryAt != null && new Date(task.retryAt).getTime() < Date.now()) { - task.scheduledAt = task.retryAt; - } else { - task.scheduledAt = task.runAt; - } - task.retryAt = claimOwnershipUntil; - task.ownerId = taskStore.taskManagerId; - task.status = TaskStatus.Claiming; + // apply capacity constraint to candidate tasks + const tasksToRun: ConcreteTaskInstance[] = []; + const leftOverTasks: ConcreteTaskInstance[] = []; + + let capacityAccumulator = 0; + for (const task of candidateTasks) { + const taskCost = definitions.get(task.taskType)?.cost ?? TaskCost.Normal; + if (capacityAccumulator + taskCost <= initialCapacity) { + tasksToRun.push(task); + capacityAccumulator += taskCost; + } else { + leftOverTasks.push(task); + capacityAccumulator = initialCapacity; + } + } - return task; + // build the updated task objects we'll claim + const taskUpdates: ConcreteTaskInstance[] = []; + for (const task of tasksToRun) { + taskUpdates.push({ + ...task, + scheduledAt: + task.retryAt != null && new Date(task.retryAt).getTime() < Date.now() + ? task.retryAt + : task.runAt, + status: TaskStatus.Claiming, + retryAt: claimOwnershipUntil, + ownerId: taskStore.taskManagerId, }); + } // perform the task object updates, deal with errors - const finalResults: ConcreteTaskInstance[] = []; + const updatedTasks: ConcreteTaskInstance[] = []; let conflicts = staleTasks.length; let bulkErrors = 0; try { - const updateResults = await taskStore.bulkUpdate(taskUpdates, { validate: false }); + const updateResults = await taskStore.bulkUpdate(taskUpdates, { + validate: false, + excludeLargeFields: true, + }); for (const updateResult of updateResults) { if (isOk(updateResult)) { - finalResults.push(updateResult.value); + updatedTasks.push(updateResult.value); } else { const { id, type, error } = updateResult.error; @@ -209,6 +233,27 @@ async function claimAvailableTasks(opts: TaskClaimerOpts): Promise<ClaimOwnershi logger.warn(`Error updating tasks during claim: ${err}`, logMeta); } + // perform an mget to get the full task instance for claiming + let fullTasksToRun: ConcreteTaskInstance[] = []; + try { + fullTasksToRun = (await taskStore.bulkGet(updatedTasks.map((task) => task.id))).reduce< + ConcreteTaskInstance[] + >((acc, task) => { + if (isOk(task)) { + acc.push(task.value); + } else { + const { id, type, error } = task.error; + logger.warn( + `Error getting full task ${id}:${type} during claim: ${error.message}`, + logMeta + ); + } + return acc; + }, []); + } catch (err) { + logger.warn(`Error getting full task documents during claim: ${err}`, logMeta); + } + // separate update for removed tasks; shouldn't happen often, so unlikely // a performance concern, and keeps the rest of the logic simpler let removedCount = 0; @@ -220,7 +265,10 @@ async function claimAvailableTasks(opts: TaskClaimerOpts): Promise<ClaimOwnershi // don't worry too much about errors, we'll get them next time try { - const removeResults = await taskStore.bulkUpdate(tasksToRemove, { validate: false }); + const removeResults = await taskStore.bulkUpdate(tasksToRemove, { + validate: false, + excludeLargeFields: true, + }); for (const removeResult of removeResults) { if (isOk(removeResult)) { removedCount++; @@ -238,21 +286,22 @@ async function claimAvailableTasks(opts: TaskClaimerOpts): Promise<ClaimOwnershi } // TODO: need a better way to generate stats - const message = `task claimer claimed: ${finalResults.length}; stale: ${staleTasks.length}; conflicts: ${conflicts}; missing: ${missingTasks.length}; updateErrors: ${bulkErrors}; removed: ${removedCount};`; + const message = `task claimer claimed: ${fullTasksToRun.length}; stale: ${staleTasks.length}; conflicts: ${conflicts}; missing: ${missingTasks.length}; capacity reached: ${leftOverTasks.length}; updateErrors: ${bulkErrors}; removed: ${removedCount};`; logger.debug(message, logMeta); // build results const finalResult = { stats: { - tasksUpdated: finalResults.length, + tasksUpdated: fullTasksToRun.length, tasksConflicted: conflicts, - tasksClaimed: finalResults.length, + tasksClaimed: fullTasksToRun.length, + tasksLeftUnclaimed: leftOverTasks.length, }, - docs: finalResults, + docs: fullTasksToRun, timing: stopTaskTimer(), }; - for (const doc of finalResults) { + for (const doc of fullTasksToRun) { events$.next(asTaskClaimEvent(doc.id, asOk(doc), finalResult.timing)); } @@ -296,12 +345,16 @@ async function searchAvailableTasks({ tasksWithPartitions(partitions) ); - return await taskStore.fetch({ - query, - sort, - size, - seq_no_primary_term: true, - }); + return await taskStore.fetch( + { + query, + sort, + size, + seq_no_primary_term: true, + }, + // limit the response size + true + ); } function applyLimitedConcurrency( diff --git a/x-pack/plugins/task_manager/server/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool.test.ts deleted file mode 100644 index 5fb1325da3df9..0000000000000 --- a/x-pack/plugins/task_manager/server/task_pool.test.ts +++ /dev/null @@ -1,471 +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 sinon from 'sinon'; -import { of, Subject } from 'rxjs'; -import { TaskPool, TaskPoolRunResult } from './task_pool'; -import { resolvable, sleep } from './test_utils'; -import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { Logger } from '@kbn/core/server'; -import { asOk } from './lib/result_type'; -import { SavedObjectsErrorHelpers } from '@kbn/core/server'; -import moment from 'moment'; -import { v4 as uuidv4 } from 'uuid'; -import { TaskRunningStage } from './task_running'; - -describe('TaskPool', () => { - beforeEach(() => { - jest.useFakeTimers(); - jest.setSystemTime(new Date(2021, 12, 30)); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - test('occupiedWorkers are a sum of running tasks', async () => { - const pool = new TaskPool({ - maxWorkers$: of(200), - logger: loggingSystemMock.create().get(), - }); - - const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); - - expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); - expect(pool.occupiedWorkers).toEqual(3); - }); - - test('availableWorkers are a function of total_capacity - occupiedWorkers', async () => { - const pool = new TaskPool({ - maxWorkers$: of(10), - logger: loggingSystemMock.create().get(), - }); - - const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); - - expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); - expect(pool.availableWorkers).toEqual(7); - }); - - test('availableWorkers is 0 until maxWorkers$ pushes a value', async () => { - const maxWorkers$ = new Subject<number>(); - const pool = new TaskPool({ - maxWorkers$, - logger: loggingSystemMock.create().get(), - }); - - expect(pool.availableWorkers).toEqual(0); - maxWorkers$.next(10); - expect(pool.availableWorkers).toEqual(10); - }); - - test('does not run tasks that are beyond its available capacity', async () => { - const pool = new TaskPool({ - maxWorkers$: of(2), - logger: loggingSystemMock.create().get(), - }); - - const shouldRun = mockRun(); - const shouldNotRun = mockRun(); - - const result = await pool.run([ - { ...mockTask(), run: shouldRun }, - { ...mockTask(), run: shouldRun }, - { ...mockTask(), run: shouldNotRun }, - ]); - - expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); - expect(pool.availableWorkers).toEqual(0); - expect(shouldRun).toHaveBeenCalledTimes(2); - expect(shouldNotRun).not.toHaveBeenCalled(); - }); - - test('should log when marking a Task as running fails', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - maxWorkers$: of(2), - logger, - }); - - const taskFailedToMarkAsRunning = mockTask(); - taskFailedToMarkAsRunning.markTaskAsRunning.mockImplementation(async () => { - throw new Error(`Mark Task as running has failed miserably`); - }); - - const result = await pool.run([mockTask(), taskFailedToMarkAsRunning, mockTask()]); - - expect((logger as jest.Mocked<Logger>).error.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "Failed to mark Task TaskType \\"shooooo\\" as running: Mark Task as running has failed miserably", - ] - `); - - expect(result).toEqual(TaskPoolRunResult.RunningAtCapacity); - }); - - test('should log when running a Task fails', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - maxWorkers$: of(3), - logger, - }); - - const taskFailedToRun = mockTask(); - taskFailedToRun.run.mockImplementation(async () => { - throw new Error(`Run Task has failed miserably`); - }); - - const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); - - expect((logger as jest.Mocked<Logger>).warn.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "Task TaskType \\"shooooo\\" failed in attempt to run: Run Task has failed miserably", - ] - `); - - expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); - }); - - test('should not log when running a Task fails due to the Task SO having been deleted while in flight', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - maxWorkers$: of(3), - logger, - }); - - const taskFailedToRun = mockTask(); - taskFailedToRun.run.mockImplementation(async () => { - throw SavedObjectsErrorHelpers.createGenericNotFoundError('task', taskFailedToRun.id); - }); - - const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); - - expect(logger.debug).toHaveBeenCalledWith( - `Task TaskType "shooooo" failed in attempt to run: Saved object [task/${taskFailedToRun.id}] not found` - ); - expect(logger.warn).not.toHaveBeenCalled(); - - expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); - }); - - test('Running a task which fails still takes up capacity', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - maxWorkers$: of(1), - logger, - }); - - const taskFailedToRun = mockTask(); - taskFailedToRun.run.mockImplementation(async () => { - await sleep(0); - throw new Error(`Run Task has failed miserably`); - }); - - const result = await pool.run([taskFailedToRun, mockTask()]); - - expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); - }); - - test('clears up capacity when a task completes', async () => { - const pool = new TaskPool({ - maxWorkers$: of(1), - logger: loggingSystemMock.create().get(), - }); - - const firstWork = resolvable(); - const firstRun = sinon.spy(async () => { - await sleep(0); - firstWork.resolve(); - return asOk({ state: {} }); - }); - const secondWork = resolvable(); - const secondRun = sinon.spy(async () => { - await sleep(0); - secondWork.resolve(); - return asOk({ state: {} }); - }); - - const result = await pool.run([ - { ...mockTask(), run: firstRun }, - { ...mockTask(), run: secondRun }, - ]); - - expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); - expect(pool.occupiedWorkers).toEqual(1); - expect(pool.availableWorkers).toEqual(0); - - await firstWork; - sinon.assert.calledOnce(firstRun); - sinon.assert.notCalled(secondRun); - - expect(pool.occupiedWorkers).toEqual(0); - await pool.run([{ ...mockTask(), run: secondRun }]); - expect(pool.occupiedWorkers).toEqual(1); - - expect(pool.availableWorkers).toEqual(0); - - await secondWork; - - expect(pool.occupiedWorkers).toEqual(0); - expect(pool.availableWorkers).toEqual(1); - sinon.assert.calledOnce(secondRun); - }); - - test('run cancels expired tasks prior to running new tasks', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - maxWorkers$: of(2), - logger, - }); - - const haltUntilWeAfterFirstRun = resolvable(); - const taskHasExpired = resolvable(); - const haltTaskSoThatItCanBeCanceled = resolvable(); - - const shouldRun = sinon.spy(() => Promise.resolve()); - const shouldNotRun = sinon.spy(() => Promise.resolve()); - const now = new Date(); - const result = await pool.run([ - { - ...mockTask({ id: '1' }), - async run() { - await haltUntilWeAfterFirstRun; - this.isExpired = true; - taskHasExpired.resolve(); - await haltTaskSoThatItCanBeCanceled; - return asOk({ state: {} }); - }, - get expiration() { - return now; - }, - get startedAt() { - // 5 and a half minutes - return moment(now).subtract(5, 'm').subtract(30, 's').toDate(); - }, - cancel: shouldRun, - }, - { - ...mockTask({ id: '2' }), - async run() { - // halt here so that we can verify that this task is counted in `occupiedWorkers` - await haltUntilWeAfterFirstRun; - return asOk({ state: {} }); - }, - cancel: shouldNotRun, - }, - ]); - - expect(result).toEqual(TaskPoolRunResult.RunningAtCapacity); - expect(pool.occupiedWorkers).toEqual(2); - expect(pool.availableWorkers).toEqual(0); - - // release first stage in task so that it has time to expire, but not complete - haltUntilWeAfterFirstRun.resolve(); - await taskHasExpired; - - expect(await pool.run([{ ...mockTask({ id: '3' }) }])).toBeTruthy(); - - sinon.assert.calledOnce(shouldRun); - sinon.assert.notCalled(shouldNotRun); - - expect(pool.occupiedWorkers).toEqual(1); - expect(pool.availableWorkers).toEqual(1); - - haltTaskSoThatItCanBeCanceled.resolve(); - - expect(logger.warn).toHaveBeenCalledWith( - `Cancelling task TaskType "shooooo" as it expired at ${now.toISOString()} after running for 05m 30s (with timeout set at 5m).` - ); - }); - - test('calls to availableWorkers ensures we cancel expired tasks', async () => { - const pool = new TaskPool({ - maxWorkers$: of(1), - logger: loggingSystemMock.create().get(), - }); - - const taskIsRunning = resolvable(); - const taskHasExpired = resolvable(); - const cancel = sinon.spy(() => Promise.resolve()); - const now = new Date(); - expect( - await pool.run([ - { - ...mockTask(), - async run() { - await sleep(10); - this.isExpired = true; - taskIsRunning.resolve(); - await taskHasExpired; - return asOk({ state: {} }); - }, - get expiration() { - return new Date(now.getTime() + 10); - }, - get startedAt() { - return now; - }, - cancel, - }, - ]) - ).toEqual(TaskPoolRunResult.RunningAtCapacity); - - await taskIsRunning; - - sinon.assert.notCalled(cancel); - expect(pool.occupiedWorkers).toEqual(1); - // The call to `availableWorkers` will clear the expired task so it's 1 instead of 0 - expect(pool.availableWorkers).toEqual(1); - sinon.assert.calledOnce(cancel); - - expect(pool.occupiedWorkers).toEqual(0); - expect(pool.availableWorkers).toEqual(1); - // ensure cancel isn't called twice - sinon.assert.calledOnce(cancel); - taskHasExpired.resolve(); - }); - - test('logs if cancellation errors', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - logger, - maxWorkers$: of(20), - }); - - const cancelled = resolvable(); - const result = await pool.run([ - { - ...mockTask(), - async run() { - this.isExpired = true; - await sleep(10); - return asOk({ state: {} }); - }, - async cancel() { - cancelled.resolve(); - throw new Error('Dern!'); - }, - toString: () => '"shooooo!"', - }, - ]); - - expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); - await pool.run([]); - - expect(pool.occupiedWorkers).toEqual(0); - - // Allow the task to cancel... - await cancelled; - - expect((logger as jest.Mocked<Logger>).error.mock.calls[0][0]).toMatchInlineSnapshot( - `"Failed to cancel task \\"shooooo!\\": Error: Dern!"` - ); - }); - - test('only allows one task with the same id in the task pool', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - maxWorkers$: of(2), - logger, - }); - - const shouldRun = mockRun(); - const shouldNotRun = mockRun(); - - const taskId = uuidv4(); - const task1 = mockTask({ id: taskId, run: shouldRun }); - const task2 = mockTask({ - id: taskId, - run: shouldNotRun, - isSameTask() { - return true; - }, - }); - - await pool.run([task1]); - await pool.run([task2]); - - expect(shouldRun).toHaveBeenCalledTimes(1); - expect(shouldNotRun).not.toHaveBeenCalled(); - }); - - // This test is from https://github.com/elastic/kibana/issues/172116 - // It's not clear how to reproduce the actual error, but it is easy to - // reproduce with the wacky test below. It does log the exact error - // from that issue, without the corresponding fix in task_pool.ts - test('works when available workers is 0 but there are tasks to run', async () => { - const logger = loggingSystemMock.create().get(); - const pool = new TaskPool({ - maxWorkers$: of(2), - logger, - }); - - const shouldRun = mockRun(); - - const taskId = uuidv4(); - const task1 = mockTask({ id: taskId, run: shouldRun }); - - // we need to alternate the values of `availableWorkers`. First it - // should be 0, then 1, then 0, then 1, etc. This will cause task_pool.run - // to partition tasks (0 to run, everything as leftover), then at the - // end of run(), to check if it should recurse, it should be > 0. - let awValue = 1; - Object.defineProperty(pool, 'availableWorkers', { - get() { - return ++awValue % 2; - }, - }); - - const result = await pool.run([task1]); - expect(result).toBe(TaskPoolRunResult.RanOutOfCapacity); - - expect((logger as jest.Mocked<Logger>).warn.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "task pool run attempts exceeded 3; assuming ran out of capacity; availableWorkers: 0, tasksToRun: 0, leftOverTasks: 1, maxWorkers: 2, occupiedWorkers: 0, workerLoad: 0", - ] - `); - }); - - function mockRun() { - return jest.fn(async () => { - await sleep(0); - return asOk({ state: {} }); - }); - } - - function mockTask(overrides = {}) { - return { - isExpired: false, - taskExecutionId: uuidv4(), - id: uuidv4(), - cancel: async () => undefined, - markTaskAsRunning: jest.fn(async () => true), - run: mockRun(), - stage: TaskRunningStage.PENDING, - toString: () => `TaskType "shooooo"`, - isAdHocTaskAndOutOfAttempts: false, - removeTask: jest.fn(), - get expiration() { - return new Date(); - }, - get startedAt() { - return new Date(); - }, - get definition() { - return { - type: '', - title: '', - timeout: '5m', - createTaskRunner: jest.fn(), - }; - }, - isSameTask() { - return false; - }, - ...overrides, - }; - } -}); diff --git a/x-pack/plugins/task_manager/server/task_pool/capacity.mock.ts b/x-pack/plugins/task_manager/server/task_pool/capacity.mock.ts new file mode 100644 index 0000000000000..ed3fd3b07f07c --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/capacity.mock.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. + */ +const createCapacityMock = () => { + return jest.fn().mockImplementation(() => { + return { + determineTasksToRunBasedOnCapacity: jest.fn(), + getUsedCapacityByType: jest.fn(), + usedCapacityPercentage: jest.fn(), + usedCapacity: jest.fn(), + capacity: jest.fn(), + }; + }); +}; + +export const capacityMock = { + create: createCapacityMock(), +}; diff --git a/x-pack/plugins/task_manager/server/task_pool/cost_capacity.test.ts b/x-pack/plugins/task_manager/server/task_pool/cost_capacity.test.ts new file mode 100644 index 0000000000000..b40c6eb2af37d --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/cost_capacity.test.ts @@ -0,0 +1,171 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { of, Subject } from 'rxjs'; +import { TaskCost } from '../task'; +import { CostCapacity } from './cost_capacity'; +import { mockTask } from './test_utils'; + +const logger = loggingSystemMock.create().get(); + +describe('CostCapacity', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('capacity responds to changes from capacity$ observable', () => { + const capacity$ = new Subject<number>(); + const pool = new CostCapacity({ capacity$, logger }); + + expect(pool.capacity).toBe(0); + + capacity$.next(20); + expect(pool.capacity).toBe(40); + + capacity$.next(16); + expect(pool.capacity).toBe(32); + + expect(logger.debug).toHaveBeenCalledTimes(2); + expect(logger.debug).toHaveBeenNthCalledWith( + 1, + `Task pool now using 40 as the max allowed cost which is based on a capacity of 20` + ); + expect(logger.debug).toHaveBeenNthCalledWith( + 2, + `Task pool now using 32 as the max allowed cost which is based on a capacity of 16` + ); + }); + + test('usedCapacity returns the sum of costs of tasks in the pool', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect(pool.usedCapacity(tasksInPool)).toBe(5); + }); + + test('usedCapacityPercentage returns the percentage of capacity used based on cost of tasks in the pool', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect(pool.usedCapacityPercentage(tasksInPool)).toBe(25); + }); + + test('usedCapacityByType returns the sum of of costs of tasks of specified type in the pool', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + + const tasksInPool = [ + { ...mockTask({}, { type: 'type1' }) }, + { ...mockTask({}, { type: 'type1', cost: TaskCost.Tiny }) }, + { ...mockTask({}, { type: 'type2' }) }, + ]; + + expect(pool.getUsedCapacityByType(tasksInPool, 'type1')).toBe(3); + expect(pool.getUsedCapacityByType(tasksInPool, 'type2')).toBe(2); + expect(pool.getUsedCapacityByType(tasksInPool, 'type3')).toBe(0); + }); + + test('availableCapacity returns the full available capacity when no task type is defined', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect(pool.availableCapacity(tasksInPool)).toBe(15); + }); + + test('availableCapacity returns the full available capacity when task type with no maxConcurrency is provided', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect( + pool.availableCapacity(tasksInPool, { + type: 'type1', + cost: TaskCost.Normal, + createTaskRunner: jest.fn(), + timeout: '5m', + }) + ).toBe(15); + }); + + test('availableCapacity returns the available capacity for the task type when task type with maxConcurrency is provided', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask({}, { type: 'type1' }) }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect( + pool.availableCapacity(tasksInPool, { + type: 'type1', + maxConcurrency: 3, + cost: TaskCost.Normal, + createTaskRunner: jest.fn(), + timeout: '5m', + }) + ).toBe(4); + }); + + describe('determineTasksToRunBasedOnCapacity', () => { + test('runs all tasks if there is capacity', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + const tasks = [{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]; + const [tasksToRun, leftoverTasks] = pool.determineTasksToRunBasedOnCapacity(tasks, 20); + + expect(tasksToRun).toEqual(tasks); + expect(leftoverTasks).toEqual([]); + }); + + test('runs task in order until capacity is reached', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + const tasks = [ + { ...mockTask() }, + { ...mockTask() }, + { ...mockTask() }, + { ...mockTask({}, { cost: TaskCost.ExtraLarge }) }, + { ...mockTask({}, { cost: TaskCost.ExtraLarge }) }, + // technically have capacity for these tasks if we skip the previous task, but we're running + // in order to avoid possibly starving large cost tasks + { ...mockTask() }, + { ...mockTask() }, + ]; + const [tasksToRun, leftoverTasks] = pool.determineTasksToRunBasedOnCapacity(tasks, 20); + + expect(tasksToRun).toEqual([tasks[0], tasks[1], tasks[2], tasks[3]]); + expect(leftoverTasks).toEqual([tasks[4], tasks[5], tasks[6]]); + }); + + test('does not run tasks if there is no capacity', () => { + const pool = new CostCapacity({ capacity$: of(10), logger }); + const tasks = [{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]; + const [tasksToRun, leftoverTasks] = pool.determineTasksToRunBasedOnCapacity(tasks, 1); + + expect(tasksToRun).toEqual([]); + expect(leftoverTasks).toEqual(tasks); + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/task_pool/cost_capacity.ts b/x-pack/plugins/task_manager/server/task_pool/cost_capacity.ts new file mode 100644 index 0000000000000..ead7cf1839714 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/cost_capacity.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/core/server'; +import { TaskDefinition } from '../task'; +import { TaskRunner } from '../task_running'; +import { CapacityOpts, ICapacity } from './types'; +import { getCapacityInCost } from './utils'; + +export class CostCapacity implements ICapacity { + private maxAllowedCost: number = 0; + private logger: Logger; + + constructor(opts: CapacityOpts) { + this.logger = opts.logger; + opts.capacity$.subscribe((capacity) => { + // Capacity config describes the number of normal-cost tasks that can be + // run simulatenously. Multiple by the cost of a normal cost to determine + // the maximum allowed cost + this.maxAllowedCost = getCapacityInCost(capacity); + this.logger.debug( + `Task pool now using ${this.maxAllowedCost} as the max allowed cost which is based on a capacity of ${capacity}` + ); + }); + } + + public get capacity(): number { + return this.maxAllowedCost; + } + + /** + * Gets how much capacity is currently in use. + */ + public usedCapacity(tasksInPool: Map<string, TaskRunner>) { + let result = 0; + tasksInPool.forEach((task) => { + if (task.definition?.cost) { + result += task.definition.cost; + } + }); + return result; + } + + /** + * Gets % of capacity in use + */ + public usedCapacityPercentage(tasksInPool: Map<string, TaskRunner>) { + return this.capacity ? Math.round((this.usedCapacity(tasksInPool) * 100) / this.capacity) : 100; + } + + /** + * Gets how much capacity is currently in use by each type. + */ + public getUsedCapacityByType(tasksInPool: TaskRunner[], type: string) { + return tasksInPool.reduce( + (count, runningTask) => + runningTask.definition?.type === type ? count + runningTask.definition.cost : count, + 0 + ); + } + + public availableCapacity( + tasksInPool: Map<string, TaskRunner>, + taskDefinition?: TaskDefinition | null + ): number { + const allAvailableCapacity = this.capacity - this.usedCapacity(tasksInPool); + if (taskDefinition && taskDefinition.maxConcurrency) { + // calculate the max capacity that can be used for this task type based on cost + const maxCapacityForType = taskDefinition.maxConcurrency * taskDefinition.cost; + return Math.max( + Math.min( + allAvailableCapacity, + maxCapacityForType - + this.getUsedCapacityByType([...tasksInPool.values()], taskDefinition.type) + ), + 0 + ); + } + + return allAvailableCapacity; + } + + public determineTasksToRunBasedOnCapacity( + tasks: TaskRunner[], + availableCapacity: number + ): [TaskRunner[], TaskRunner[]] { + const tasksToRun: TaskRunner[] = []; + const leftOverTasks: TaskRunner[] = []; + + let capacityAccumulator = 0; + for (const task of tasks) { + const taskCost = task.definition?.cost ?? 0; + if (capacityAccumulator + taskCost <= availableCapacity) { + tasksToRun.push(task); + capacityAccumulator += taskCost; + } else { + leftOverTasks.push(task); + // Don't claim further tasks even if lower cost tasks are next. + // It may be an extra large task and we need to make room for it + // for the next claiming cycle + capacityAccumulator = availableCapacity; + } + } + + return [tasksToRun, leftOverTasks]; + } +} diff --git a/x-pack/plugins/task_manager/server/task_pool/index.ts b/x-pack/plugins/task_manager/server/task_pool/index.ts new file mode 100644 index 0000000000000..979a4536639a6 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/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 { TaskPool, TaskPoolRunResult } from './task_pool'; +export { getCapacityInCost, getCapacityInWorkers } from './utils'; diff --git a/x-pack/plugins/task_manager/server/task_pool.mock.ts b/x-pack/plugins/task_manager/server/task_pool/task_pool.mock.ts similarity index 58% rename from x-pack/plugins/task_manager/server/task_pool.mock.ts rename to x-pack/plugins/task_manager/server/task_pool/task_pool.mock.ts index 77568c8c6cdfa..00c3cfae16317 100644 --- a/x-pack/plugins/task_manager/server/task_pool.mock.ts +++ b/x-pack/plugins/task_manager/server/task_pool/task_pool.mock.ts @@ -8,16 +8,14 @@ import { TaskPool } from './task_pool'; const defaultGetCapacityOverride: () => Partial<{ load: number; - occupiedWorkers: number; - workerLoad: number; - max: number; - availableWorkers: number; + usedCapacity: number; + usedCapacityPercentage: number; + availableCapacity: number; }> = () => ({ load: 0, - occupiedWorkers: 0, - workerLoad: 0, - max: 10, - availableWorkers: 10, + usedCapacity: 0, + usedCapacityPercentage: 0, + availableCapacity: 20, }); const createTaskPoolMock = (getCapacityOverride = defaultGetCapacityOverride) => { @@ -25,19 +23,16 @@ const createTaskPoolMock = (getCapacityOverride = defaultGetCapacityOverride) => get load() { return getCapacityOverride().load ?? 0; }, - get occupiedWorkers() { - return getCapacityOverride().occupiedWorkers ?? 0; + get usedCapacity() { + return getCapacityOverride().usedCapacity ?? 0; }, - get workerLoad() { - return getCapacityOverride().workerLoad ?? 0; + get usedCapacityPercentage() { + return getCapacityOverride().usedCapacityPercentage ?? 0; }, - get max() { - return getCapacityOverride().max ?? 10; + availableCapacity() { + return getCapacityOverride().availableCapacity ?? 20; }, - get availableWorkers() { - return getCapacityOverride().availableWorkers ?? 10; - }, - getOccupiedWorkersByType: jest.fn(), + getUsedCapacityByType: jest.fn(), run: jest.fn(), cancelRunningTasks: jest.fn(), } as unknown as jest.Mocked<TaskPool>; diff --git a/x-pack/plugins/task_manager/server/task_pool/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool/task_pool.test.ts new file mode 100644 index 0000000000000..e2936b7ccec0a --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/task_pool.test.ts @@ -0,0 +1,867 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import sinon from 'sinon'; +import { of, Subject } from 'rxjs'; +import { TaskPool, TaskPoolRunResult } from './task_pool'; +import { resolvable, sleep } from '../test_utils'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { Logger } from '@kbn/core/server'; +import { asOk } from '../lib/result_type'; +import { SavedObjectsErrorHelpers } from '@kbn/core/server'; +import moment from 'moment'; +import { v4 as uuidv4 } from 'uuid'; +import { TaskCost } from '../task'; +import * as CostCapacityModule from './cost_capacity'; +import * as WorkerCapacityModule from './worker_capacity'; +import { capacityMock } from './capacity.mock'; +import { CLAIM_STRATEGY_DEFAULT, CLAIM_STRATEGY_MGET } from '../config'; +import { mockRun, mockTask } from './test_utils'; +import { TaskTypeDictionary } from '../task_type_dictionary'; + +jest.mock('../constants', () => ({ + CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE: ['report', 'quickReport'], +})); + +describe('TaskPool', () => { + const costCapacityMock = capacityMock.create(); + const workerCapacityMock = capacityMock.create(); + const logger = loggingSystemMock.create().get(); + + const definitions = new TaskTypeDictionary(logger); + definitions.registerTaskDefinitions({ + report: { + title: 'report', + maxConcurrency: 1, + cost: TaskCost.ExtraLarge, + createTaskRunner: jest.fn(), + }, + quickReport: { + title: 'quickReport', + maxConcurrency: 5, + createTaskRunner: jest.fn(), + }, + }); + + beforeEach(() => { + jest.resetAllMocks(); + jest.useFakeTimers(); + jest.setSystemTime(new Date(2021, 12, 30)); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('uses the correct capacity calculator based on the strategy', () => { + let costCapacitySpy: jest.SpyInstance; + let workerCapacitySpy: jest.SpyInstance; + beforeEach(() => { + costCapacitySpy = jest + .spyOn(CostCapacityModule, 'CostCapacity') + .mockImplementation(() => costCapacityMock); + + workerCapacitySpy = jest + .spyOn(WorkerCapacityModule, 'WorkerCapacity') + .mockImplementation(() => workerCapacityMock); + }); + + afterEach(() => { + costCapacitySpy.mockRestore(); + workerCapacitySpy.mockRestore(); + }); + + test('uses CostCapacity to calculate capacity when strategy is mget', () => { + new TaskPool({ capacity$: of(20), definitions, logger, strategy: CLAIM_STRATEGY_MGET }); + + expect(CostCapacityModule.CostCapacity).toHaveBeenCalledTimes(1); + expect(WorkerCapacityModule.WorkerCapacity).not.toHaveBeenCalled(); + }); + + test('uses WorkerCapacity to calculate capacity when strategy is default', () => { + new TaskPool({ capacity$: of(20), definitions, logger, strategy: CLAIM_STRATEGY_DEFAULT }); + + expect(CostCapacityModule.CostCapacity).not.toHaveBeenCalled(); + expect(WorkerCapacityModule.WorkerCapacity).toHaveBeenCalledTimes(1); + }); + + test('uses WorkerCapacity to calculate capacity when strategy is unrecognized', () => { + new TaskPool({ capacity$: of(20), definitions, logger, strategy: 'any old strategy' }); + + expect(CostCapacityModule.CostCapacity).not.toHaveBeenCalled(); + expect(WorkerCapacityModule.WorkerCapacity).toHaveBeenCalledTimes(1); + }); + }); + + describe('with CLAIM_STRATEGY_DEFAULT', () => { + test('usedCapacity is the number running tasks', async () => { + const pool = new TaskPool({ + capacity$: of(10), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + expect(pool.usedCapacity).toEqual(3); + }); + + test('availableCapacity are a function of total_capacity - usedCapacity', async () => { + const pool = new TaskPool({ + capacity$: of(10), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + expect(pool.availableCapacity()).toEqual(7); + }); + + test('availableCapacity is 0 until capacity$ pushes a value', async () => { + const capacity$ = new Subject<number>(); + const pool = new TaskPool({ + capacity$, + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + expect(pool.availableCapacity()).toEqual(0); + capacity$.next(10); + expect(pool.availableCapacity()).toEqual(10); + }); + + test('does not run tasks that are beyond its available capacity', async () => { + const pool = new TaskPool({ + capacity$: of(2), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const shouldRun = mockRun(); + const shouldNotRun = mockRun(); + + const result = await pool.run([ + { ...mockTask(), run: shouldRun }, + { ...mockTask(), run: shouldRun }, + { ...mockTask(), run: shouldNotRun }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); + expect(pool.availableCapacity()).toEqual(0); + expect(shouldRun).toHaveBeenCalledTimes(2); + expect(shouldNotRun).not.toHaveBeenCalled(); + }); + + test('should log when marking a Task as running fails', async () => { + const pool = new TaskPool({ + capacity$: of(3), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const taskFailedToMarkAsRunning = mockTask(); + taskFailedToMarkAsRunning.markTaskAsRunning.mockImplementation(async () => { + throw new Error(`Mark Task as running has failed miserably`); + }); + + const result = await pool.run([mockTask(), taskFailedToMarkAsRunning, mockTask()]); + + expect((logger as jest.Mocked<Logger>).error.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Failed to mark Task TaskType \\"shooooo\\" as running: Mark Task as running has failed miserably", + ] + `); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('should log when running a Task fails', async () => { + const pool = new TaskPool({ + capacity$: of(3), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + throw new Error(`Run Task has failed miserably`); + }); + + const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); + + expect((logger as jest.Mocked<Logger>).warn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Task TaskType \\"shooooo\\" failed in attempt to run: Run Task has failed miserably", + ] + `); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('should not log when running a Task fails due to the Task SO having been deleted while in flight', async () => { + const pool = new TaskPool({ + capacity$: of(3), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + throw SavedObjectsErrorHelpers.createGenericNotFoundError('task', taskFailedToRun.id); + }); + + const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); + + expect(logger.debug).toHaveBeenCalledWith( + `Task TaskType "shooooo" failed in attempt to run: Saved object [task/${taskFailedToRun.id}] not found` + ); + expect(logger.warn).not.toHaveBeenCalled(); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('Running a task which fails still takes up capacity', async () => { + const pool = new TaskPool({ + capacity$: of(1), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + await sleep(0); + throw new Error(`Run Task has failed miserably`); + }); + + const result = await pool.run([taskFailedToRun, mockTask()]); + + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); + }); + + test('clears up capacity when a task completes', async () => { + const pool = new TaskPool({ + capacity$: of(1), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const firstWork = resolvable(); + const firstRun = sinon.spy(async () => { + await sleep(0); + firstWork.resolve(); + return asOk({ state: {} }); + }); + const secondWork = resolvable(); + const secondRun = sinon.spy(async () => { + await sleep(0); + secondWork.resolve(); + return asOk({ state: {} }); + }); + + const result = await pool.run([ + { ...mockTask(), run: firstRun }, + { ...mockTask(), run: secondRun }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); + expect(pool.usedCapacity).toEqual(1); + expect(pool.availableCapacity()).toEqual(0); + + await firstWork; + sinon.assert.calledOnce(firstRun); + sinon.assert.notCalled(secondRun); + + expect(pool.usedCapacity).toEqual(0); + await pool.run([{ ...mockTask(), run: secondRun }]); + expect(pool.usedCapacity).toEqual(1); + + expect(pool.availableCapacity()).toEqual(0); + + await secondWork; + + expect(pool.usedCapacity).toEqual(0); + expect(pool.availableCapacity()).toEqual(1); + sinon.assert.calledOnce(secondRun); + }); + + test('run cancels expired tasks prior to running new tasks', async () => { + const pool = new TaskPool({ + capacity$: of(2), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const haltUntilWeAfterFirstRun = resolvable(); + const taskHasExpired = resolvable(); + const haltTaskSoThatItCanBeCanceled = resolvable(); + + const shouldRun = sinon.spy(() => Promise.resolve()); + const shouldNotRun = sinon.spy(() => Promise.resolve()); + const now = new Date(); + const result = await pool.run([ + { + ...mockTask({ id: '1' }), + async run() { + await haltUntilWeAfterFirstRun; + this.isExpired = true; + taskHasExpired.resolve(); + await haltTaskSoThatItCanBeCanceled; + return asOk({ state: {} }); + }, + get expiration() { + return now; + }, + get startedAt() { + // 5 and a half minutes + return moment(now).subtract(5, 'm').subtract(30, 's').toDate(); + }, + cancel: shouldRun, + }, + { + ...mockTask({ id: '2' }), + async run() { + // halt here so that we can verify that this task is counted in `occupiedWorkers` + await haltUntilWeAfterFirstRun; + return asOk({ state: {} }); + }, + cancel: shouldNotRun, + }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RunningAtCapacity); + expect(pool.usedCapacity).toEqual(2); + expect(pool.availableCapacity()).toEqual(0); + + // release first stage in task so that it has time to expire, but not complete + haltUntilWeAfterFirstRun.resolve(); + await taskHasExpired; + + expect(await pool.run([{ ...mockTask({ id: '3' }) }])).toBeTruthy(); + + sinon.assert.calledOnce(shouldRun); + sinon.assert.notCalled(shouldNotRun); + + expect(pool.usedCapacity).toEqual(1); + expect(pool.availableCapacity()).toEqual(1); + + haltTaskSoThatItCanBeCanceled.resolve(); + + expect(logger.warn).toHaveBeenCalledWith( + `Cancelling task TaskType "shooooo" as it expired at ${now.toISOString()} after running for 05m 30s (with timeout set at 5m).` + ); + }); + + test('calls to availableWorkers ensures we cancel expired tasks', async () => { + const pool = new TaskPool({ + capacity$: of(1), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const taskIsRunning = resolvable(); + const taskHasExpired = resolvable(); + const cancel = sinon.spy(() => Promise.resolve()); + const now = new Date(); + expect( + await pool.run([ + { + ...mockTask(), + async run() { + await sleep(10); + this.isExpired = true; + taskIsRunning.resolve(); + await taskHasExpired; + return asOk({ state: {} }); + }, + get expiration() { + return new Date(now.getTime() + 10); + }, + get startedAt() { + return now; + }, + cancel, + }, + ]) + ).toEqual(TaskPoolRunResult.RunningAtCapacity); + + await taskIsRunning; + + sinon.assert.notCalled(cancel); + expect(pool.usedCapacity).toEqual(1); + // The call to `availableCapacity` will clear the expired task so it's 1 instead of 0 + expect(pool.availableCapacity()).toEqual(1); + sinon.assert.calledOnce(cancel); + + expect(pool.usedCapacity).toEqual(0); + expect(pool.availableCapacity()).toEqual(1); + // ensure cancel isn't called twice + sinon.assert.calledOnce(cancel); + taskHasExpired.resolve(); + }); + + test('logs if cancellation errors', async () => { + const pool = new TaskPool({ + capacity$: of(10), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const cancelled = resolvable(); + const result = await pool.run([ + { + ...mockTask(), + async run() { + this.isExpired = true; + await sleep(10); + return asOk({ state: {} }); + }, + async cancel() { + cancelled.resolve(); + throw new Error('Dern!'); + }, + toString: () => '"shooooo!"', + }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + await pool.run([]); + + expect(pool.usedCapacity).toEqual(0); + + // Allow the task to cancel... + await cancelled; + + expect((logger as jest.Mocked<Logger>).error.mock.calls[0][0]).toMatchInlineSnapshot( + `"Failed to cancel task \\"shooooo!\\": Error: Dern!"` + ); + }); + + test('only allows one task with the same id in the task pool', async () => { + const pool = new TaskPool({ + capacity$: of(2), + definitions, + logger, + strategy: CLAIM_STRATEGY_DEFAULT, + }); + + const shouldRun = mockRun(); + const shouldNotRun = mockRun(); + + const taskId = uuidv4(); + const task1 = mockTask({ id: taskId, run: shouldRun }); + const task2 = mockTask({ + id: taskId, + run: shouldNotRun, + isSameTask() { + return true; + }, + }); + + await pool.run([task1]); + await pool.run([task2]); + + expect(shouldRun).toHaveBeenCalledTimes(1); + expect(shouldNotRun).not.toHaveBeenCalled(); + }); + }); + + describe('with CLAIM_STRATEGY_MGET', () => { + test('usedCapacity is the sum of the cost of running tasks', async () => { + const pool = new TaskPool({ + capacity$: of(10), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + expect(pool.usedCapacity).toEqual(3 * TaskCost.Normal); + }); + + test('availableCapacity are a function of total_capacity - usedCapacity', async () => { + const pool = new TaskPool({ + capacity$: of(10), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const result = await pool.run([{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + expect(pool.availableCapacity()).toEqual(14); + }); + + test('availableCapacity is 0 until capacity$ pushes a value', async () => { + const capacity$ = new Subject<number>(); + const pool = new TaskPool({ capacity$, definitions, logger, strategy: CLAIM_STRATEGY_MGET }); + + expect(pool.availableCapacity()).toEqual(0); + capacity$.next(20); + expect(pool.availableCapacity()).toEqual(40); + }); + + test('does not run tasks that are beyond its available capacity', async () => { + const pool = new TaskPool({ + capacity$: of(2), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const shouldRun = mockRun(); + const shouldNotRun = mockRun(); + + const result = await pool.run([ + { ...mockTask(), run: shouldRun }, + { ...mockTask(), run: shouldRun }, + { ...mockTask(), run: shouldNotRun }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); + expect(pool.availableCapacity()).toEqual(0); + expect(shouldRun).toHaveBeenCalledTimes(2); + expect(shouldNotRun).not.toHaveBeenCalled(); + }); + + test('should log when marking a Task as running fails', async () => { + const pool = new TaskPool({ + capacity$: of(6), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const taskFailedToMarkAsRunning = mockTask(); + taskFailedToMarkAsRunning.markTaskAsRunning.mockImplementation(async () => { + throw new Error(`Mark Task as running has failed miserably`); + }); + + const result = await pool.run([mockTask(), taskFailedToMarkAsRunning, mockTask()]); + + expect((logger as jest.Mocked<Logger>).error.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Failed to mark Task TaskType \\"shooooo\\" as running: Mark Task as running has failed miserably", + ] + `); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('should log when running a Task fails', async () => { + const pool = new TaskPool({ + capacity$: of(3), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + throw new Error(`Run Task has failed miserably`); + }); + + const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); + + expect((logger as jest.Mocked<Logger>).warn.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "Task TaskType \\"shooooo\\" failed in attempt to run: Run Task has failed miserably", + ] + `); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('should not log when running a Task fails due to the Task SO having been deleted while in flight', async () => { + const pool = new TaskPool({ + capacity$: of(3), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + throw SavedObjectsErrorHelpers.createGenericNotFoundError('task', taskFailedToRun.id); + }); + + const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); + + expect(logger.debug).toHaveBeenCalledWith( + `Task TaskType "shooooo" failed in attempt to run: Saved object [task/${taskFailedToRun.id}] not found` + ); + expect(logger.warn).not.toHaveBeenCalled(); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + }); + + test('Running a task which fails still takes up capacity', async () => { + const pool = new TaskPool({ + capacity$: of(1), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const taskFailedToRun = mockTask(); + taskFailedToRun.run.mockImplementation(async () => { + await sleep(0); + throw new Error(`Run Task has failed miserably`); + }); + + const result = await pool.run([taskFailedToRun, mockTask()]); + + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); + }); + + test('clears up capacity when a task completes', async () => { + const pool = new TaskPool({ + capacity$: of(1), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const firstWork = resolvable(); + const firstRun = sinon.spy(async () => { + await sleep(0); + firstWork.resolve(); + return asOk({ state: {} }); + }); + const secondWork = resolvable(); + const secondRun = sinon.spy(async () => { + await sleep(0); + secondWork.resolve(); + return asOk({ state: {} }); + }); + + const result = await pool.run([ + { ...mockTask(), run: firstRun }, + { ...mockTask(), run: secondRun }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RanOutOfCapacity); + expect(pool.usedCapacity).toEqual(2); + expect(pool.availableCapacity()).toEqual(0); + + await firstWork; + sinon.assert.calledOnce(firstRun); + sinon.assert.notCalled(secondRun); + + expect(pool.usedCapacity).toEqual(0); + await pool.run([{ ...mockTask(), run: secondRun }]); + expect(pool.usedCapacity).toEqual(2); + + expect(pool.availableCapacity()).toEqual(0); + + await secondWork; + + expect(pool.usedCapacity).toEqual(0); + expect(pool.availableCapacity()).toEqual(2); + sinon.assert.calledOnce(secondRun); + }); + + test('run cancels expired tasks prior to running new tasks', async () => { + const pool = new TaskPool({ + capacity$: of(2), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const haltUntilWeAfterFirstRun = resolvable(); + const taskHasExpired = resolvable(); + const haltTaskSoThatItCanBeCanceled = resolvable(); + + const shouldRun = sinon.spy(() => Promise.resolve()); + const shouldNotRun = sinon.spy(() => Promise.resolve()); + const now = new Date(); + const result = await pool.run([ + { + ...mockTask({ id: '1' }), + async run() { + await haltUntilWeAfterFirstRun; + this.isExpired = true; + taskHasExpired.resolve(); + await haltTaskSoThatItCanBeCanceled; + return asOk({ state: {} }); + }, + get expiration() { + return now; + }, + get startedAt() { + // 5 and a half minutes + return moment(now).subtract(5, 'm').subtract(30, 's').toDate(); + }, + cancel: shouldRun, + }, + { + ...mockTask({ id: '2' }), + async run() { + // halt here so that we can verify that this task is counted in `occupiedWorkers` + await haltUntilWeAfterFirstRun; + return asOk({ state: {} }); + }, + cancel: shouldNotRun, + }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RunningAtCapacity); + expect(pool.usedCapacity).toEqual(4); + expect(pool.availableCapacity()).toEqual(0); + + // release first stage in task so that it has time to expire, but not complete + haltUntilWeAfterFirstRun.resolve(); + await taskHasExpired; + + expect(await pool.run([{ ...mockTask({ id: '3' }) }])).toBeTruthy(); + + sinon.assert.calledOnce(shouldRun); + sinon.assert.notCalled(shouldNotRun); + + expect(pool.usedCapacity).toEqual(2); + expect(pool.availableCapacity()).toEqual(2); + + haltTaskSoThatItCanBeCanceled.resolve(); + + expect(logger.warn).toHaveBeenCalledWith( + `Cancelling task TaskType "shooooo" as it expired at ${now.toISOString()} after running for 05m 30s (with timeout set at 5m).` + ); + }); + + test('calls to availableWorkers ensures we cancel expired tasks', async () => { + const pool = new TaskPool({ + capacity$: of(1), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const taskIsRunning = resolvable(); + const taskHasExpired = resolvable(); + const cancel = sinon.spy(() => Promise.resolve()); + const now = new Date(); + expect( + await pool.run([ + { + ...mockTask(), + async run() { + await sleep(10); + this.isExpired = true; + taskIsRunning.resolve(); + await taskHasExpired; + return asOk({ state: {} }); + }, + get expiration() { + return new Date(now.getTime() + 10); + }, + get startedAt() { + return now; + }, + cancel, + }, + ]) + ).toEqual(TaskPoolRunResult.RunningAtCapacity); + + await taskIsRunning; + + sinon.assert.notCalled(cancel); + expect(pool.usedCapacity).toEqual(2); + // The call to `availableCapacity` will clear the expired task so it's 2 instead of 0 + expect(pool.availableCapacity()).toEqual(2); + sinon.assert.calledOnce(cancel); + + expect(pool.usedCapacity).toEqual(0); + expect(pool.availableCapacity()).toEqual(2); + // ensure cancel isn't called twice + sinon.assert.calledOnce(cancel); + taskHasExpired.resolve(); + }); + + test('logs if cancellation errors', async () => { + const pool = new TaskPool({ + capacity$: of(10), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const cancelled = resolvable(); + const result = await pool.run([ + { + ...mockTask(), + async run() { + this.isExpired = true; + await sleep(10); + return asOk({ state: {} }); + }, + async cancel() { + cancelled.resolve(); + throw new Error('Dern!'); + }, + toString: () => '"shooooo!"', + }, + ]); + + expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); + await pool.run([]); + + expect(pool.usedCapacity).toEqual(0); + + // Allow the task to cancel... + await cancelled; + + expect((logger as jest.Mocked<Logger>).error.mock.calls[0][0]).toMatchInlineSnapshot( + `"Failed to cancel task \\"shooooo!\\": Error: Dern!"` + ); + }); + + test('only allows one task with the same id in the task pool', async () => { + const pool = new TaskPool({ + capacity$: of(2), + definitions, + logger, + strategy: CLAIM_STRATEGY_MGET, + }); + + const shouldRun = mockRun(); + const shouldNotRun = mockRun(); + + const taskId = uuidv4(); + const task1 = mockTask({ id: taskId, run: shouldRun }); + const task2 = mockTask({ + id: taskId, + run: shouldNotRun, + isSameTask() { + return true; + }, + }); + + await pool.run([task1]); + await pool.run([task2]); + + expect(shouldRun).toHaveBeenCalledTimes(1); + expect(shouldNotRun).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/task_pool.ts b/x-pack/plugins/task_manager/server/task_pool/task_pool.ts similarity index 73% rename from x-pack/plugins/task_manager/server/task_pool.ts rename to x-pack/plugins/task_manager/server/task_pool/task_pool.ts index d4ae8e46fbc74..abd220796a0c8 100644 --- a/x-pack/plugins/task_manager/server/task_pool.ts +++ b/x-pack/plugins/task_manager/server/task_pool/task_pool.ts @@ -13,13 +13,20 @@ import { Observable, Subject } from 'rxjs'; import moment, { Duration } from 'moment'; import { padStart } from 'lodash'; import { Logger } from '@kbn/core/server'; -import { TaskRunner } from './task_running'; -import { isTaskSavedObjectNotFoundError } from './lib/is_task_not_found_error'; -import { TaskManagerStat } from './task_events'; +import { TaskRunner } from '../task_running'; +import { isTaskSavedObjectNotFoundError } from '../lib/is_task_not_found_error'; +import { TaskManagerStat } from '../task_events'; +import { ICapacity } from './types'; +import { CLAIM_STRATEGY_MGET } from '../config'; +import { WorkerCapacity } from './worker_capacity'; +import { CostCapacity } from './cost_capacity'; +import { TaskTypeDictionary } from '../task_type_dictionary'; -interface Opts { - maxWorkers$: Observable<number>; +interface TaskPoolOpts { + capacity$: Observable<number>; + definitions: TaskTypeDictionary; logger: Logger; + strategy: string; } export enum TaskPoolRunResult { @@ -34,31 +41,43 @@ export enum TaskPoolRunResult { } const VERSION_CONFLICT_MESSAGE = 'Task has been claimed by another Kibana service'; -const MAX_RUN_ATTEMPTS = 3; /** * Runs tasks in batches, taking costs into account. */ export class TaskPool { - private maxWorkers: number = 0; private tasksInPool = new Map<string, TaskRunner>(); private logger: Logger; private load$ = new Subject<TaskManagerStat>(); + private definitions: TaskTypeDictionary; + private capacityCalculator: ICapacity; /** * Creates an instance of TaskPool. * * @param {Opts} opts - * @prop {number} maxWorkers - The total number of workers / work slots available - * (e.g. maxWorkers is 4, then 2 tasks of cost 2 can run at a time, or 4 tasks of cost 1) + * @prop {number} capacity - The total capacity available + * (e.g. capacity is 4, then 2 tasks of cost 2 can run at a time, or 4 tasks of cost 1) * @prop {Logger} logger - The task manager logger. */ - constructor(opts: Opts) { + constructor(opts: TaskPoolOpts) { this.logger = opts.logger; - opts.maxWorkers$.subscribe((maxWorkers) => { - this.logger.debug(`Task pool now using ${maxWorkers} as the max worker value`); - this.maxWorkers = maxWorkers; - }); + this.definitions = opts.definitions; + + switch (opts.strategy) { + case CLAIM_STRATEGY_MGET: + this.capacityCalculator = new CostCapacity({ + capacity$: opts.capacity$, + logger: this.logger, + }); + break; + + default: + this.capacityCalculator = new WorkerCapacity({ + capacity$: opts.capacity$, + logger: this.logger, + }); + } } public get load(): Observable<TaskManagerStat> { @@ -66,38 +85,39 @@ export class TaskPool { } /** - * Gets how many workers are currently in use. + * Gets how much capacity is currently in use. */ - public get occupiedWorkers() { - return this.tasksInPool.size; + public get usedCapacity() { + return this.capacityCalculator.usedCapacity(this.tasksInPool); } /** - * Gets % of workers in use + * Gets how much capacity is currently in use as a percentage */ - public get workerLoad() { - return this.maxWorkers ? Math.round((this.occupiedWorkers * 100) / this.maxWorkers) : 100; + public get usedCapacityPercentage() { + return this.capacityCalculator.usedCapacityPercentage(this.tasksInPool); } /** - * Gets how many workers are currently available. + * Gets how much capacity is currently available. */ - public get availableWorkers() { + public availableCapacity(taskType?: string) { // cancel expired task whenever a call is made to check for capacity // this ensures that we don't end up with a queue of hung tasks causing both // the poller and the pool from hanging due to lack of capacity this.cancelExpiredTasks(); - return this.maxWorkers - this.occupiedWorkers; + + return this.capacityCalculator.availableCapacity( + this.tasksInPool, + taskType ? this.definitions.get(taskType) : null + ); } /** - * Gets how many workers are currently in use by type. + * Gets how much capacity is currently in use by each type. */ - public getOccupiedWorkersByType(type: string) { - return [...this.tasksInPool.values()].reduce( - (count, runningTask) => (runningTask.definition?.type === type ? ++count : count), - 0 - ); + public getUsedCapacityByType(type: string) { + return this.capacityCalculator.getUsedCapacityByType([...this.tasksInPool.values()], type); } /** @@ -108,26 +128,14 @@ export class TaskPool { * @param {TaskRunner[]} tasks * @returns {Promise<boolean>} */ - public async run(tasks: TaskRunner[], attempt = 1): Promise<TaskPoolRunResult> { - // Note `this.availableWorkers` is a getter with side effects, so we just want + public async run(tasks: TaskRunner[]): Promise<TaskPoolRunResult> { + // Note `this.availableCapacity` has side effects, so we just want // to call it once for this bit of the code. - const availableWorkers = this.availableWorkers; - const [tasksToRun, leftOverTasks] = partitionListByCount(tasks, availableWorkers); - - if (attempt > MAX_RUN_ATTEMPTS) { - const stats = [ - `availableWorkers: ${availableWorkers}`, - `tasksToRun: ${tasksToRun.length}`, - `leftOverTasks: ${leftOverTasks.length}`, - `maxWorkers: ${this.maxWorkers}`, - `occupiedWorkers: ${this.occupiedWorkers}`, - `workerLoad: ${this.workerLoad}`, - ].join(', '); - this.logger.warn( - `task pool run attempts exceeded ${MAX_RUN_ATTEMPTS}; assuming ran out of capacity; ${stats}` - ); - return TaskPoolRunResult.RanOutOfCapacity; - } + const availableCapacity = this.availableCapacity(); + const [tasksToRun, leftOverTasks] = this.capacityCalculator.determineTasksToRunBasedOnCapacity( + tasks, + availableCapacity + ); if (tasksToRun.length) { await Promise.all( @@ -163,11 +171,10 @@ export class TaskPool { } if (leftOverTasks.length) { - if (this.availableWorkers) { - return this.run(leftOverTasks, attempt + 1); - } + // leave any leftover tasks + // they will be available for claiming in 30 seconds return TaskPoolRunResult.RanOutOfCapacity; - } else if (!this.availableWorkers) { + } else if (!this.availableCapacity()) { return TaskPoolRunResult.RunningAtCapacity; } return TaskPoolRunResult.RunningAllClaimedTasks; @@ -242,11 +249,6 @@ export class TaskPool { } } -function partitionListByCount<T>(list: T[], count: number): [T[], T[]] { - const listInCount = list.splice(0, count); - return [listInCount, list]; -} - function durationAsString(duration: Duration): string { const [m, s] = [duration.minutes(), duration.seconds()].map((value) => padStart(`${value}`, 2, '0') diff --git a/x-pack/plugins/task_manager/server/task_pool/test_utils.ts b/x-pack/plugins/task_manager/server/task_pool/test_utils.ts new file mode 100644 index 0000000000000..b518ed7b8f8f5 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/test_utils.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 { v4 as uuidv4 } from 'uuid'; +import { asOk } from '../lib/result_type'; +import { sleep } from '../test_utils'; +import { TaskRunningStage } from '../task_running'; +import { TaskCost } from '../task'; + +export function mockRun() { + return jest.fn(async () => { + await sleep(0); + return asOk({ state: {} }); + }); +} + +export function mockTask(overrides = {}, definitionOverrides = {}) { + return { + isExpired: false, + taskExecutionId: uuidv4(), + id: uuidv4(), + cancel: async () => undefined, + markTaskAsRunning: jest.fn(async () => true), + run: mockRun(), + stage: TaskRunningStage.PENDING, + toString: () => `TaskType "shooooo"`, + isAdHocTaskAndOutOfAttempts: false, + removeTask: jest.fn(), + get expiration() { + return new Date(); + }, + get startedAt() { + return new Date(); + }, + get definition() { + return { + type: '', + title: '', + timeout: '5m', + cost: TaskCost.Normal, + createTaskRunner: jest.fn(), + ...definitionOverrides, + }; + }, + isSameTask() { + return false; + }, + ...overrides, + }; +} diff --git a/x-pack/plugins/task_manager/server/task_pool/types.ts b/x-pack/plugins/task_manager/server/task_pool/types.ts new file mode 100644 index 0000000000000..759af4f6d6e70 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/types.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 { Observable } from 'rxjs'; +import { Logger } from '@kbn/core/server'; +import { TaskRunner } from '../task_running'; +import { TaskDefinition } from '../task'; + +export interface ICapacity { + get capacity(): number; + availableCapacity( + tasksInPool: Map<string, TaskRunner>, + taskDefinition?: TaskDefinition | null + ): number; + usedCapacity(tasksInPool: Map<string, TaskRunner>): number; + usedCapacityPercentage(tasksInPool: Map<string, TaskRunner>): number; + getUsedCapacityByType(tasksInPool: TaskRunner[], type: string): number; + determineTasksToRunBasedOnCapacity( + tasks: TaskRunner[], + availableCapacity: number + ): [TaskRunner[], TaskRunner[]]; +} + +export interface CapacityOpts { + capacity$: Observable<number>; + logger: Logger; +} diff --git a/x-pack/plugins/task_manager/server/task_pool/utils.ts b/x-pack/plugins/task_manager/server/task_pool/utils.ts new file mode 100644 index 0000000000000..d4c89be46e02d --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/utils.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 { TaskCost } from '../task'; + +// When configured capacity is the number of normal cost tasks that this Kibana +// can run, the total available workers equals the capacity +export const getCapacityInWorkers = (capacity: number) => capacity; + +// When configured capacity is the number of normal cost tasks that this Kibana +// can run, the total available cost equals the capacity multiplied by the cost of a normal task +export const getCapacityInCost = (capacity: number) => capacity * TaskCost.Normal; diff --git a/x-pack/plugins/task_manager/server/task_pool/worker_capacity.test.ts b/x-pack/plugins/task_manager/server/task_pool/worker_capacity.test.ts new file mode 100644 index 0000000000000..7ed7485ccdd52 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/worker_capacity.test.ts @@ -0,0 +1,176 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { of, Subject } from 'rxjs'; +import { TaskCost } from '../task'; +import { mockTask } from './test_utils'; +import { WorkerCapacity } from './worker_capacity'; + +const logger = loggingSystemMock.create().get(); + +describe('WorkerCapacity', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('workers set based on capacity responds to changes from capacity$ observable', () => { + const capacity$ = new Subject<number>(); + const pool = new WorkerCapacity({ capacity$, logger }); + + expect(pool.capacity).toBe(0); + + capacity$.next(20); + expect(pool.capacity).toBe(20); + + capacity$.next(16); + expect(pool.capacity).toBe(16); + + capacity$.next(25); + expect(pool.capacity).toBe(25); + + expect(logger.debug).toHaveBeenCalledTimes(3); + expect(logger.debug).toHaveBeenNthCalledWith( + 1, + 'Task pool now using 20 as the max worker value which is based on a capacity of 20' + ); + expect(logger.debug).toHaveBeenNthCalledWith( + 2, + 'Task pool now using 16 as the max worker value which is based on a capacity of 16' + ); + expect(logger.debug).toHaveBeenNthCalledWith( + 3, + 'Task pool now using 25 as the max worker value which is based on a capacity of 25' + ); + }); + + test('usedCapacity returns the number of tasks in the pool', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect(pool.usedCapacity(tasksInPool)).toBe(3); + }); + + test('usedCapacityPercentage returns the percentage of workers in use by tasks in the pool', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect(pool.usedCapacityPercentage(tasksInPool)).toBe(30); + }); + + test('usedCapacityByType returns the number of tasks of specified type in the pool', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + + const tasksInPool = [ + { ...mockTask({}, { type: 'type1' }) }, + { ...mockTask({}, { type: 'type1', cost: TaskCost.Tiny }) }, + { ...mockTask({}, { type: 'type2' }) }, + ]; + + expect(pool.getUsedCapacityByType(tasksInPool, 'type1')).toBe(2); + expect(pool.getUsedCapacityByType(tasksInPool, 'type2')).toBe(1); + expect(pool.getUsedCapacityByType(tasksInPool, 'type3')).toBe(0); + }); + + test('availableCapacity returns the overall number of available workers when no task type is defined', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect(pool.availableCapacity(tasksInPool)).toBe(7); + }); + + test('availableCapacity returns the overall number of available workers when task type with no maxConcurrency is provided', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask() }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect( + pool.availableCapacity(tasksInPool, { + type: 'type1', + cost: TaskCost.Normal, + createTaskRunner: jest.fn(), + timeout: '5m', + }) + ).toBe(7); + }); + + test('availableCapacity returns the number of available workers for the task type when task type with maxConcurrency is provided', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + + const tasksInPool = new Map([ + ['1', { ...mockTask({}, { type: 'type1' }) }], + ['2', { ...mockTask({}, { cost: TaskCost.Tiny }) }], + ['3', { ...mockTask() }], + ]); + + expect( + pool.availableCapacity(tasksInPool, { + type: 'type1', + maxConcurrency: 3, + cost: TaskCost.Normal, + createTaskRunner: jest.fn(), + timeout: '5m', + }) + ).toBe(2); + }); + + describe('determineTasksToRunBasedOnCapacity', () => { + test('runs all tasks if there are workers available', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + const tasks = [{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]; + const [tasksToRun, leftoverTasks] = pool.determineTasksToRunBasedOnCapacity(tasks, 10); + + expect(tasksToRun).toEqual(tasks); + expect(leftoverTasks).toEqual([]); + }); + + test('splits tasks if there are more tasks than available workers', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + const tasks = [ + { ...mockTask() }, + { ...mockTask() }, + { ...mockTask() }, + { ...mockTask({}, { cost: TaskCost.ExtraLarge }) }, + { ...mockTask({}, { cost: TaskCost.ExtraLarge }) }, + { ...mockTask() }, + { ...mockTask() }, + ]; + const [tasksToRun, leftoverTasks] = pool.determineTasksToRunBasedOnCapacity(tasks, 5); + + expect(tasksToRun).toEqual([tasks[0], tasks[1], tasks[2], tasks[3], tasks[4]]); + expect(leftoverTasks).toEqual([tasks[5], tasks[6]]); + }); + + test('does not run tasks if there is no capacity', () => { + const pool = new WorkerCapacity({ capacity$: of(10), logger }); + const tasks = [{ ...mockTask() }, { ...mockTask() }, { ...mockTask() }]; + const [tasksToRun, leftoverTasks] = pool.determineTasksToRunBasedOnCapacity(tasks, 0); + + expect(tasksToRun).toEqual([]); + expect(leftoverTasks).toEqual(tasks); + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/task_pool/worker_capacity.ts b/x-pack/plugins/task_manager/server/task_pool/worker_capacity.ts new file mode 100644 index 0000000000000..8363c53f58ec1 --- /dev/null +++ b/x-pack/plugins/task_manager/server/task_pool/worker_capacity.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/core/server'; +import { TaskRunner } from '../task_running'; +import { CapacityOpts, ICapacity } from './types'; +import { TaskDefinition } from '../task'; +import { getCapacityInWorkers } from './utils'; + +export class WorkerCapacity implements ICapacity { + private workers: number = 0; + private logger: Logger; + + constructor(opts: CapacityOpts) { + this.logger = opts.logger; + opts.capacity$.subscribe((capacity) => { + // Capacity config describes the number of normal-cost tasks that can be + // run simulatenously. This directly corresponds to the number of workers to use. + this.workers = getCapacityInWorkers(capacity); + this.logger.debug( + `Task pool now using ${this.workers} as the max worker value which is based on a capacity of ${capacity}` + ); + }); + } + + public get capacity(): number { + return this.workers; + } + + /** + * Gets how many workers are currently in use. + */ + public usedCapacity(tasksInPool: Map<string, TaskRunner>) { + return tasksInPool.size; + } + + /** + * Gets % of workers in use + */ + public usedCapacityPercentage(tasksInPool: Map<string, TaskRunner>) { + return this.capacity ? Math.round((this.usedCapacity(tasksInPool) * 100) / this.capacity) : 100; + } + + /** + * Gets how many workers are currently in use by each type. + */ + public getUsedCapacityByType(tasksInPool: TaskRunner[], type: string) { + return tasksInPool.reduce( + (count, runningTask) => (runningTask.definition?.type === type ? ++count : count), + 0 + ); + } + + public availableCapacity( + tasksInPool: Map<string, TaskRunner>, + taskDefinition?: TaskDefinition | null + ): number { + const allAvailableCapacity = this.capacity - this.usedCapacity(tasksInPool); + if (taskDefinition && taskDefinition.maxConcurrency) { + // calculate the max workers that can be used for this task type + return Math.max( + Math.min( + allAvailableCapacity, + taskDefinition.maxConcurrency - + this.getUsedCapacityByType([...tasksInPool.values()], taskDefinition.type) + ), + 0 + ); + } + + return allAvailableCapacity; + } + + public determineTasksToRunBasedOnCapacity( + tasks: TaskRunner[], + availableCapacity: number + ): [TaskRunner[], TaskRunner[]] { + const tasksToRun: TaskRunner[] = []; + const leftOverTasks: TaskRunner[] = []; + + for (let i = 0; i < tasks.length; i++) { + if (i >= availableCapacity) { + leftOverTasks.push(tasks[i]); + } else { + tasksToRun.push(tasks[i]); + } + } + + return [tasksToRun, leftOverTasks]; + } +} diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index afde0ae91ea55..9bc1a64140647 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -8,7 +8,7 @@ import { schema } from '@kbn/config-schema'; import { Client } from '@elastic/elasticsearch'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import _ from 'lodash'; +import _, { omit } from 'lodash'; import { first } from 'rxjs'; import { @@ -18,7 +18,7 @@ import { SerializedConcreteTaskInstance, } from './task'; import { elasticsearchServiceMock, savedObjectsServiceMock } from '@kbn/core/server/mocks'; -import { TaskStore, SearchOpts, AggregationOpts } from './task_store'; +import { TaskStore, SearchOpts, AggregationOpts, taskInstanceToAttributes } from './task_store'; import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; import { SavedObjectAttributes, SavedObjectsErrorHelpers } from '@kbn/core/server'; import { TaskTypeDictionary } from './task_type_dictionary'; @@ -292,12 +292,16 @@ describe('TaskStore', () => { }); }); - async function testFetch(opts?: SearchOpts, hits: Array<estypes.SearchHit<unknown>> = []) { + async function testFetch( + opts?: SearchOpts, + hits: Array<estypes.SearchHit<unknown>> = [], + limitResponse: boolean = false + ) { childEsClient.search.mockResponse({ hits: { hits, total: hits.length }, } as estypes.SearchResponse); - const result = await store.fetch(opts); + const result = await store.fetch(opts, limitResponse); expect(childEsClient.search).toHaveBeenCalledTimes(1); @@ -342,6 +346,18 @@ describe('TaskStore', () => { await expect(store.fetch()).rejects.toThrowErrorMatchingInlineSnapshot(`"Failure"`); expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); }); + + test('excludes state and params from source when excludeState is true', async () => { + const { args } = await testFetch({}, [], true); + expect(args).toMatchObject({ + index: 'tasky', + body: { + sort: [{ 'task.runAt': 'asc' }], + query: { term: { type: 'task' } }, + }, + _source_excludes: ['task.state', 'task.params'], + }); + }); }); describe('aggregate', () => { @@ -615,10 +631,11 @@ describe('TaskStore', () => { describe('bulkUpdate', () => { let store: TaskStore; + const logger = mockLogger(); beforeAll(() => { store = new TaskStore({ - logger: mockLogger(), + logger, index: 'tasky', taskManagerId: '', serializer, @@ -671,6 +688,125 @@ describe('TaskStore', () => { expect(mockGetValidatedTaskInstanceForUpdating).toHaveBeenCalledWith(task, { validate: false, }); + + expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledWith( + [ + { + id: task.id, + type: 'task', + version: task.version, + attributes: taskInstanceToAttributes(task, task.id), + }, + ], + { refresh: false } + ); + }); + + test(`validates whenever validate:true is passed-in`, async () => { + const task = { + runAt: mockedDate, + scheduledAt: mockedDate, + startedAt: null, + retryAt: null, + id: 'task:324242', + params: { hello: 'world' }, + state: { foo: 'bar' }, + taskType: 'report', + attempts: 3, + status: 'idle' as TaskStatus, + version: '123', + ownerId: null, + traceparent: '', + }; + + savedObjectsClient.bulkUpdate.mockResolvedValue({ + saved_objects: [ + { + id: '324242', + type: 'task', + attributes: { + ...task, + state: '{"foo":"bar"}', + params: '{"hello":"world"}', + }, + references: [], + version: '123', + }, + ], + }); + + await store.bulkUpdate([task], { validate: true }); + + expect(mockGetValidatedTaskInstanceForUpdating).toHaveBeenCalledWith(task, { + validate: true, + }); + + expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledWith( + [ + { + id: task.id, + type: 'task', + version: task.version, + attributes: taskInstanceToAttributes(task, task.id), + }, + ], + { refresh: false } + ); + }); + + test(`logs warning and doesn't validate whenever excludeLargeFields option is passed-in`, async () => { + const task = { + runAt: mockedDate, + scheduledAt: mockedDate, + startedAt: null, + retryAt: null, + id: 'task:324242', + params: { hello: 'world' }, + state: { foo: 'bar' }, + taskType: 'report', + attempts: 3, + status: 'idle' as TaskStatus, + version: '123', + ownerId: null, + traceparent: '', + }; + + savedObjectsClient.bulkUpdate.mockResolvedValue({ + saved_objects: [ + { + id: '324242', + type: 'task', + attributes: { + ...task, + state: '{"foo":"bar"}', + params: '{"hello":"world"}', + }, + references: [], + version: '123', + }, + ], + }); + + await store.bulkUpdate([task], { validate: true, excludeLargeFields: true }); + + expect(logger.warn).toHaveBeenCalledWith( + `Skipping validation for bulk update because excludeLargeFields=true.` + ); + expect(mockGetValidatedTaskInstanceForUpdating).toHaveBeenCalledWith(task, { + validate: false, + }); + + expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledWith( + [ + { + id: task.id, + type: 'task', + version: task.version, + attributes: omit(taskInstanceToAttributes(task, task.id), ['state', 'params']), + }, + ], + { refresh: false } + ); }); test('pushes error from saved objects client to errors$', async () => { diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts index e0ad3dfae149a..9b58d7bc3c18b 100644 --- a/x-pack/plugins/task_manager/server/task_store.ts +++ b/x-pack/plugins/task_manager/server/task_store.ts @@ -84,6 +84,11 @@ export interface FetchResult { versionMap: Map<string, ConcreteTaskInstanceVersion>; } +export interface BulkUpdateOpts { + validate: boolean; + excludeLargeFields?: boolean; +} + export type BulkUpdateResult = Result< ConcreteTaskInstance, { type: string; id: string; error: SavedObjectError } @@ -108,6 +113,7 @@ export class TaskStore { public readonly taskManagerId: string; public readonly errors$ = new Subject<Error>(); public readonly taskValidator: TaskValidator; + private readonly logger: Logger; private esClient: ElasticsearchClient; private esClientWithoutRetries: ElasticsearchClient; @@ -134,6 +140,7 @@ export class TaskStore { this.serializer = opts.serializer; this.savedObjectsRepository = opts.savedObjectsRepository; this.adHocTaskCounter = opts.adHocTaskCounter; + this.logger = opts.logger; this.taskValidator = new TaskValidator({ logger: opts.logger, definitions: opts.definitions, @@ -232,15 +239,13 @@ export class TaskStore { * Fetches a list of scheduled tasks with default sorting. * * @param opts - The query options used to filter tasks + * @param limitResponse - Whether to exclude the task state and params from the source for a smaller respose payload */ - public async fetch({ - sort = [{ 'task.runAt': 'asc' }], - ...opts - }: SearchOpts = {}): Promise<FetchResult> { - return this.search({ - ...opts, - sort, - }); + public async fetch( + { sort = [{ 'task.runAt': 'asc' }], ...opts }: SearchOpts = {}, + limitResponse: boolean = false + ): Promise<FetchResult> { + return this.search({ ...opts, sort }, limitResponse); } /** @@ -296,13 +301,23 @@ export class TaskStore { */ public async bulkUpdate( docs: ConcreteTaskInstance[], - options: { validate: boolean } + { validate, excludeLargeFields = false }: BulkUpdateOpts ): Promise<BulkUpdateResult[]> { + // if we're excluding large fields (state and params), we cannot apply validation so log a warning + if (validate && excludeLargeFields) { + validate = false; + this.logger.warn(`Skipping validation for bulk update because excludeLargeFields=true.`); + } + const attributesByDocId = docs.reduce((attrsById, doc) => { const taskInstance = this.taskValidator.getValidatedTaskInstanceForUpdating(doc, { - validate: options.validate, + validate, }); - attrsById.set(doc.id, taskInstanceToAttributes(taskInstance, doc.id)); + const taskAttributes = taskInstanceToAttributes(taskInstance, doc.id); + attrsById.set( + doc.id, + excludeLargeFields ? omit(taskAttributes, 'state', 'params') : taskAttributes + ); return attrsById; }, new Map()); @@ -342,7 +357,7 @@ export class TaskStore { ), }); const result = this.taskValidator.getValidatedTaskInstanceFromReading(taskInstance, { - validate: options.validate, + validate, }); return asOk(result); }); @@ -489,18 +504,20 @@ export class TaskStore { } } - private async search(opts: SearchOpts = {}): Promise<FetchResult> { + private async search( + opts: SearchOpts = {}, + limitResponse: boolean = false + ): Promise<FetchResult> { const { query } = ensureQueryOnlyReturnsTaskObjects(opts); try { const result = await this.esClientWithoutRetries.search<SavedObjectsRawDoc['_source']>({ index: this.index, ignore_unavailable: true, - body: { - ...opts, - query, - }, + body: { ...opts, query }, + ...(limitResponse ? { _source_excludes: ['task.state', 'task.params'] } : {}), }); + const { hits: { hits: tasks }, } = result; @@ -627,7 +644,10 @@ export function correctVersionConflictsForContinuation( return maxDocs && versionConflicts + updated > maxDocs ? maxDocs - updated : versionConflicts; } -function taskInstanceToAttributes(doc: TaskInstance, id: string): SerializedConcreteTaskInstance { +export function taskInstanceToAttributes( + doc: TaskInstance, + id: string +): SerializedConcreteTaskInstance { return { ...omit(doc, 'id', 'version'), params: JSON.stringify(doc.params || {}), diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts index f2faeec4b9877..7be758be03567 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts @@ -6,7 +6,7 @@ */ import { get } from 'lodash'; -import { RunContext, TaskDefinition, TaskPriority } from './task'; +import { RunContext, TaskCost, TaskDefinition, TaskPriority } from './task'; import { mockLogger } from './test_utils'; import { sanitizeTaskDefinitions, @@ -53,6 +53,7 @@ describe('taskTypeDictionary', () => { const logger = mockLogger(); beforeEach(() => { + jest.resetAllMocks(); definitions = new TaskTypeDictionary(logger); }); @@ -64,6 +65,7 @@ describe('taskTypeDictionary', () => { expect(result).toMatchInlineSnapshot(` Array [ Object { + "cost": 2, "createTaskRunner": [Function], "description": "one super cool task", "timeout": "5m", @@ -71,6 +73,7 @@ describe('taskTypeDictionary', () => { "type": "test_task_type_0", }, Object { + "cost": 2, "createTaskRunner": [Function], "description": "one super cool task", "timeout": "5m", @@ -78,6 +81,7 @@ describe('taskTypeDictionary', () => { "type": "test_task_type_1", }, Object { + "cost": 2, "createTaskRunner": [Function], "description": "one super cool task", "timeout": "5m", @@ -225,6 +229,7 @@ describe('taskTypeDictionary', () => { createTaskRunner: expect.any(Function), maxConcurrency: 2, priority: 1, + cost: 2, timeout: '5m', title: 'foo', type: 'foo', @@ -247,6 +252,41 @@ describe('taskTypeDictionary', () => { expect(definitions.get('foo')).toEqual(undefined); }); + it('uses task cost if specified', () => { + definitions.registerTaskDefinitions({ + foo: { + title: 'foo', + maxConcurrency: 2, + cost: TaskCost.ExtraLarge, + createTaskRunner: jest.fn(), + }, + }); + expect(definitions.get('foo')).toEqual({ + createTaskRunner: expect.any(Function), + maxConcurrency: 2, + cost: 10, + timeout: '5m', + title: 'foo', + type: 'foo', + }); + }); + + it('does not register task with invalid cost schema', () => { + definitions.registerTaskDefinitions({ + foo: { + title: 'foo', + maxConcurrency: 2, + // @ts-expect-error upgrade typescript v5.1.6 + cost: 23, + createTaskRunner: jest.fn(), + }, + }); + expect(logger.error).toHaveBeenCalledWith( + `Could not sanitize task definitions: Invalid cost \"23\". Cost must be one of Tiny => 1,Normal => 2,ExtraLarge => 10` + ); + expect(definitions.get('foo')).toEqual(undefined); + }); + it('throws error when registering duplicate task type', () => { definitions.registerTaskDefinitions({ foo: { diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.ts index 85c6f3ad28eb6..e0b28eccea3cb 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.ts @@ -7,7 +7,13 @@ import { ObjectType } from '@kbn/config-schema'; import { Logger } from '@kbn/core/server'; -import { TaskDefinition, taskDefinitionSchema, TaskRunCreatorFunction, TaskPriority } from './task'; +import { + TaskDefinition, + taskDefinitionSchema, + TaskRunCreatorFunction, + TaskPriority, + TaskCost, +} from './task'; import { CONCURRENCY_ALLOW_LIST_BY_TASK_TYPE } from './constants'; /** @@ -50,6 +56,10 @@ export interface TaskRegisterDefinition { * claimed before low priority */ priority?: TaskPriority; + /** + * An optional definition of the cost associated with running the task. + */ + cost?: TaskCost; /** * An optional more detailed description of what this task does. */ diff --git a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts index 019d8bd47c57a..067a32c8a486d 100644 --- a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts +++ b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.test.ts @@ -174,7 +174,8 @@ function getMockMonitoredHealth(overrides = {}): MonitoredHealth { timestamp: new Date().toISOString(), status: HealthStatus.OK, value: { - max_workers: 10, + capacity: { config: 10, as_cost: 20, as_workers: 10 }, + claim_strategy: 'default', poll_interval: 3000, request_capacity: 1000, monitored_aggregated_stats_refresh_rate: 5000, @@ -193,16 +194,19 @@ function getMockMonitoredHealth(overrides = {}): MonitoredHealth { status: HealthStatus.OK, value: { count: 4, + cost: 8, task_types: { - actions_telemetry: { count: 2, status: { idle: 2 } }, - alerting_telemetry: { count: 1, status: { idle: 1 } }, - session_cleanup: { count: 1, status: { idle: 1 } }, + actions_telemetry: { count: 2, cost: 4, status: { idle: 2 } }, + alerting_telemetry: { count: 1, cost: 2, status: { idle: 1 } }, + session_cleanup: { count: 1, cost: 2, status: { idle: 1 } }, }, schedule: [], overdue: 0, + overdue_cost: 0, overdue_non_recurring: 0, estimatedScheduleDensity: [], non_recurring: 20, + non_recurring_cost: 40, owner_ids: 2, estimated_schedule_density: [], capacity_requirements: { diff --git a/x-pack/plugins/task_manager/tsconfig.json b/x-pack/plugins/task_manager/tsconfig.json index 5ae81e9097114..232ab3a36f2c8 100644 --- a/x-pack/plugins/task_manager/tsconfig.json +++ b/x-pack/plugins/task_manager/tsconfig.json @@ -25,7 +25,8 @@ "@kbn/alerting-state-types", "@kbn/core-saved-objects-api-server", "@kbn/logging", - "@kbn/core-lifecycle-server" + "@kbn/core-lifecycle-server", + "@kbn/cloud-plugin" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 39024e4592e11..11c1a0a7edee0 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -11279,6 +11279,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled query rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled query rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of query rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of query rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of query rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of query rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of query rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of query rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of query rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11337,6 +11399,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled threshold rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled threshold rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of threshold rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of threshold rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of threshold rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of threshold rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of threshold rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of threshold rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of threshold rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11395,6 +11519,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled eql rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled eql rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of eql rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of eql rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of eql rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of eql rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of eql rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of eql rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of eql rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11453,6 +11639,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled machine_learning rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled machine_learning rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of machine_learning rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of machine_learning rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of machine_learning rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of machine_learning rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of machine_learning rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of machine_learning rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of machine_learning rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11511,6 +11759,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled threat_match rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled threat_match rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of threat_match rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of threat_match rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of threat_match rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of threat_match rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of threat_match rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of threat_match rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of threat_match rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11569,6 +11879,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled new_terms rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled new_terms rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of new_terms rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11627,6 +11999,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled esql rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled esql rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of esql rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of esql rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of esql rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of esql rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of esql rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of esql rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of esql rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11685,6 +12119,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled elastic rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled elastic rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of elastic rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of elastic rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of elastic rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of elastic rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of elastic rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of elastic rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of elastic rules configured do not suppress alerts with missing fields" + } + } + } } } }, @@ -11743,6 +12239,68 @@ "_meta": { "description": "Number of rules using the legacy investigation fields type introduced only in 8.10 ESS" } + }, + "alert_suppression": { + "properties": { + "enabled": { + "type": "long", + "_meta": { + "description": "Number of enabled custom rules configured with suppression" + } + }, + "disabled": { + "type": "long", + "_meta": { + "description": "Number of disabled custom rules configured with suppression" + } + }, + "suppressed_fields_count": { + "properties": { + "one": { + "type": "long", + "_meta": { + "description": "Number of custom rules configured with one suppression field" + } + }, + "two": { + "type": "long", + "_meta": { + "description": "Number of custom rules configured with two suppression field" + } + }, + "three": { + "type": "long", + "_meta": { + "description": "Number of custom rules configured with three suppression field" + } + } + } + }, + "suppressed_per_time_period": { + "type": "long", + "_meta": { + "description": "Number of custom rules configured with suppression per time period" + } + }, + "suppressed_per_rule_execution": { + "type": "long", + "_meta": { + "description": "Number of custom rules configured with suppression per rule execution" + } + }, + "suppresses_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of custom rules configured to suppress alerts with missing fields" + } + }, + "does_not_suppress_missing_fields": { + "type": "long", + "_meta": { + "description": "Number of custom rules configured do not suppress alerts with missing fields" + } + } + } } } } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index cb42054921577..1396ba78da37a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -104,7 +104,7 @@ "alertsUIShared.components.alertLifecycleStatusBadge.flappingLabel": "Bagotement", "alertsUIShared.components.alertLifecycleStatusBadge.recoveredLabel": "Récupéré", "alertsUIShared.components.alertLifecycleStatusBadge.untrackedLabel": "Non suivi", - "alertsUIShared.hooks.useAlertDataView.useAlertDataMessage": "Impossible de charger la vue des données de l'alerte", + "alertsUIShared.hooks.useAlertDataView.fetchErrorMessage": "Impossible de charger la vue des données de l'alerte", "alertsUIShared.hooks.useLoadRuleTypesQuery.unableToLoadRuleTypesMessage": "Impossible de charger les types de règles", "alertsUIShared.hooks.useRuleAADFields.errorMessage": "Impossible de charger les champs d'alerte par type de règle", "alertsUIShared.maintenanceWindowCallout.fetchError": "La vérification visant à déterminer si les fenêtres de maintenance sont actives a échoué", @@ -2405,6 +2405,11 @@ "discover.embeddable.search.displayName": "rechercher", "discover.errorCalloutShowErrorMessage": "Afficher les détails", "discover.esqlMode.selectedColumnsCallout": "Affichage de {selectedColumnsNumber} champs sur {esqlQueryColumnsNumber}. Ajoutez-en d’autres depuis la liste des champs disponibles.", + "discover.esqlToDataViewTransitionModal.closeButtonLabel": "Basculer sans sauvegarder", + "discover.esqlToDataViewTransitionModal.dismissButtonLabel": "Ne plus afficher cet avertissement", + "discover.esqlToDataViewTransitionModal.saveButtonLabel": "Sauvegarder et basculer", + "discover.esqlToDataViewTransitionModal.title": "Votre requête sera supprimée", + "discover.esqlToDataviewTransitionModalBody": "Modifier la vue de données supprime la requête ES|QL en cours. Sauvegardez cette recherche pour ne pas perdre de travail.", "discover.fieldChooser.availableFieldsTooltip": "Champs disponibles pour l'affichage dans le tableau.", "discover.fieldChooser.discoverField.addFieldTooltip": "Ajouter le champ en tant que colonne", "discover.fieldChooser.discoverField.removeFieldTooltip": "Supprimer le champ du tableau", @@ -5159,10 +5164,7 @@ "kbn-esql-validation-autocomplete.esql.validation.expectedConstantValue": "L'argument de [{fn}] doit être une constante, reçu [{given}]", "kbn-esql-validation-autocomplete.esql.validation.metadataBracketsDeprecation": "Les crochets \"[]\" doivent être supprimés de la déclaration FROM METADATA", "kbn-esql-validation-autocomplete.esql.validation.missingFunction": "Fonction inconnue [{name}]", - "kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues": "Impossible de combiner les valeurs agrégées et non agrégées dans [STATS], [{expression}] trouvé", "kbn-esql-validation-autocomplete.esql.validation.noNestedArgumentSupport": "Les paramètres de la fonction agrégée doivent être un attribut, un littéral ou une fonction non agrégée ; trouvé [{name}] de type [{argType}]", - "kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction": "Au moins une fonction d'agrégation requise dans [STATS], [{expression}] trouvé", - "kbn-esql-validation-autocomplete.esql.validation.statsNoArguments": "[STATS] doit contenir au moins une expression d'agrégation ou de regroupement", "kbn-esql-validation-autocomplete.esql.validation.typeOverwrite": "La colonne [{field}] de type {fieldType} a été écrasée par un nouveau type : {newType}", "kbn-esql-validation-autocomplete.esql.validation.unknowAggregateFunction": "Attendait une fonction ou un groupe agrégé mais a obtenu [{value}] de type [{type}]", "kbn-esql-validation-autocomplete.esql.validation.unknownColumn": "Colonne inconnue[{name}]", @@ -7219,11 +7221,6 @@ "unifiedSearch.query.queryBar.indexPattern.findFilterSet": "Trouver une requête", "unifiedSearch.query.queryBar.indexPattern.manageFieldButton": "Gérer cette vue de données", "unifiedSearch.query.queryBar.indexPattern.temporaryDataviewLabel": "Temporaire", - "discover.esqlToDataviewTransitionModalBody": "Modifier la vue de données supprime la requête ES|QL en cours. Sauvegardez cette recherche pour ne pas perdre de travail.", - "discover.esqlToDataViewTransitionModal.closeButtonLabel": "Basculer sans sauvegarder", - "discover.esqlToDataViewTransitionModal.dismissButtonLabel": "Ne plus afficher cet avertissement", - "discover.esqlToDataViewTransitionModal.saveButtonLabel": "Sauvegarder et basculer", - "discover.esqlToDataViewTransitionModal.title": "Votre requête sera supprimée", "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "documents", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "Ne plus afficher", @@ -13210,7 +13207,6 @@ "xpack.csp.emptyState.resetFiltersButton": "Réinitialiser les filtres", "xpack.csp.emptyState.title": "Aucun résultat ne correspond à vos critères de recherche.", "xpack.csp.enableBenchmarkRuleButton": "Activer la règle", - "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "Affichage de {pageStart}-{pageEnd} sur {total} {type}", "xpack.csp.findings.distributionBar.totalFailedLabel": "Échec des résultats", "xpack.csp.findings.distributionBar.totalPassedLabel": "Réussite des résultats", "xpack.csp.findings.errorCallout.pageSearchErrorTitle": "Une erreur s’est produite lors de la récupération des résultats de recherche.", @@ -13249,9 +13245,6 @@ "xpack.csp.findings.gcpIntegration.gcpInputText.credentialFileText": "Chemin vers le fichier JSON qui contient les informations d'identification et la clé utilisés pour souscrire", "xpack.csp.findings.gcpIntegration.gcpInputText.credentialJSONText": "Blob JSON qui contient les informations d'identification et la clé utilisées pour souscrire", "xpack.csp.findings.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "Informations d'identification", - "xpack.csp.findings.groupBySelector.groupByLabel": "Regrouper par", - "xpack.csp.findings.groupBySelector.groupByNoneLabel": "Aucun", - "xpack.csp.findings.groupBySelector.groupByResourceIdLabel": "Ressource", "xpack.csp.findings.grouping.cloudAccount.nullGroupTitle": "Aucun compte cloud", "xpack.csp.findings.grouping.default.nullGroupTitle": "Aucun regroupement", "xpack.csp.findings.grouping.kubernetes.nullGroupTitle": "Aucun cluster Kubernetes", @@ -13441,9 +13434,6 @@ "xpack.csp.vulnerabilities.grouping.nullGroupUnit": "vulnérabilités", "xpack.csp.vulnerabilities.grouping.resource.nullGroupTitle": "Aucune ressource", "xpack.csp.vulnerabilities.grouping.severity": "Sévérité", - "xpack.csp.vulnerabilities.searchBar.placeholder": "Rechercher des vulnérabilités (par exemple vulnerability.severity : \"CRITICAL\" )", - "xpack.csp.vulnerabilities.table.filterIn": "Inclure", - "xpack.csp.vulnerabilities.table.filterOut": "Exclure", "xpack.csp.vulnerabilities.unit": "{totalCount, plural, =1 {vulnérabilité} other {vulnérabilités}}", "xpack.csp.vulnerabilities.vulnerabilitiesFindingFlyout.flyoutDescriptionList.packageTitle": "Pack", "xpack.csp.vulnerabilities.vulnerabilitiesFindingFlyout.flyoutDescriptionList.resourceId": "ID ressource", @@ -13468,8 +13458,6 @@ "xpack.csp.vulnerabilityDashboard.trendGraphChart.accountsDropDown.prepend.accountsTitle": "Comptes", "xpack.csp.vulnerabilityDashboard.trendGraphChart.trendBySeverityTitle": "Tendance par degré de gravité", "xpack.csp.vulnerabilityDashboard.viewAllButton.buttonTitle": "Tout afficher", - "xpack.csp.vulnerabilityTable.column.sortAscending": "Basse -> Critique", - "xpack.csp.vulnerabilityTable.column.sortDescending": "Critique -> Basse", "xpack.csp.vulnerabilityTable.panel.buttonText": "Afficher toutes les vulnérabilités", "xpack.csp.vulnMgmtIntegration.awsOption.nameTitle": "Amazon Web Services", "xpack.csp.vulnMgmtIntegration.azureOption.nameTitle": "Azure", @@ -22340,7 +22328,6 @@ "xpack.infra.waffle.maxGroupByTooltip": "Seuls deux regroupements peuvent être sélectionnés en même temps", "xpack.infra.waffle.metriclabel": "Indicateur", "xpack.infra.waffle.metricOptions.countText": "compte", - "xpack.infra.waffle.metricOptions.cpuUsageText": "Utilisation CPU", "xpack.infra.waffle.metricOptions.diskIOReadBytes": "lectures du disque", "xpack.infra.waffle.metricOptions.diskIOWriteBytes": "écritures sur le disque", "xpack.infra.waffle.metricOptions.hostLogRateText": "taux de log", @@ -24326,13 +24313,8 @@ "xpack.lists.services.items.fileUploadFromFileSystem": "Fichier chargé depuis le système de fichiers de {fileName}", "xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.available": "Traces d'appel disponibles", "xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.notAvailable": "Traces d'appel indisponibles", - "xpack.logsExplorer.dataTable.header.actions.tooltip.expand": "Développer les détails du log", - "xpack.logsExplorer.dataTable.header.actions.tooltip.paragraph": "Les champs fournissant des informations exploitables, comme :", - "xpack.logsExplorer.dataTable.header.actions.tooltip.stacktrace": "L'accès aux traces d'appel disponibles est basé sur :", "xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "Affiche le {logLevel} du document et les champs {message}.", "xpack.logsExplorer.dataTable.header.content.tooltip.paragraph2": "Lorsque le champ de message est vide, l'une des informations suivantes s'affiche :", - "xpack.logsExplorer.dataTable.header.popover.actions": "Actions", - "xpack.logsExplorer.dataTable.header.popover.actions.lowercase": "actions", "xpack.logsExplorer.dataTable.header.popover.content": "Contenu", "xpack.logsExplorer.dataTable.header.popover.resource": "Ressource", "xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "Les champs fournissant des informations sur la source du document, comme :", @@ -28304,7 +28286,6 @@ "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "Le modèle a des pipelines associés", "xpack.ml.trainedModels.modelsList.downloadFailed": "Échec du téléchargement de \"{modelId}\"", "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "Échec de la vérification du statut du téléchargement", - "xpack.ml.trainedModels.modelsList.downloadSuccess": "Le téléchargement du modèle \"{modelId}\" a bien été démarré.", "xpack.ml.trainedModels.modelsList.e5Title": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimisé for linux-x86_64", @@ -28362,7 +28343,6 @@ "xpack.ml.trainedModels.modelsList.pipelines.processorStats.timePerDocHeader": "Temps par document", "xpack.ml.trainedModels.modelsList.pipelines.processorStats.typeHeader": "Type de processeur", "xpack.ml.trainedModels.modelsList.recommendedDownloadContent": "Version du modèle recommandée pour la configuration matérielle de votre cluster", - "xpack.ml.trainedModels.modelsList.recommendedDownloadLabel": "(Recommandée)", "xpack.ml.trainedModels.modelsList.selectableMessage": "Sélectionner un modèle", "xpack.ml.trainedModels.modelsList.selectedModelsMessage": "{modelsCount, plural, one{# modèle sélectionné} other {# modèles sélectionnés}}", "xpack.ml.trainedModels.modelsList.startDeployment.cancelButton": "Annuler", @@ -37507,9 +37487,6 @@ "xpack.securitySolution.ruleExceptions.allExceptionItems.noSearchResultsPromptTitle": "Aucun résultat ne correspond à vos critères de recherche.", "xpack.securitySolution.ruleExceptions.allExceptionItems.paginationAriaLabel": "Pagination du tableau d'éléments d'exception", "xpack.securitySolution.ruleExceptions.allExceptionItems.searchPlaceholder": "Exceptions de filtre utilisant une syntaxe de requête simple, par exemple, le nom :\"ma liste\"", - "xpack.securitySolution.ruleExceptions.editException.cancel": "Annuler", - "xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle": "Modifier une exception de point de terminaison", - "xpack.securitySolution.ruleExceptions.editException.editExceptionTitle": "Modifier une exception à une règle", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastErrorTitle": "Erreur lors de la mise à jour de l'exception", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessText": "{numItems, plural, =1 {L'exception} other {Les exceptions}} - {exceptionItemName} - {numItems, plural, =1 {a été mise à jour} other {ont été mises à jour}}.", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle": "Exception à la règle mise à jour", @@ -42943,7 +42920,6 @@ "xpack.triggersActionsUI.updateApiKeyConfirmModal.failureMessage": "Impossible de mettre à jour {idsToUpdate, plural, one {la clé} other {les clés}} de l'API", "xpack.triggersActionsUI.updateApiKeyConfirmModal.title": "Mettre à jour la clé d'API", "xpack.triggersActionsUI.urlSyncedAlertsSearchBar.invalidQueryTitle": "Chaîne de requête non valide", - "xpack.triggersActionsUI.useAlertDataView.useAlertDataMessage": "Impossible de charger la vue des données de l'alerte", "xpack.triggersActionsUI.useRuleAADFields.errorMessage": "Impossible de charger les champs d'alerte par type de règle", "xpack.upgradeAssistant.app.deniedPrivilegeDescription": "Afin d'utiliser l'assistant de mise à niveau et de résoudre les problèmes de déclassement, vous devez disposer d'un accès permettant de gérer tous les espaces Kibana.", "xpack.upgradeAssistant.app.deniedPrivilegeTitle": "Rôle d'administrateur Kibana requis", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 37c5d38512fe8..de9f53757bd7e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -104,7 +104,7 @@ "alertsUIShared.components.alertLifecycleStatusBadge.flappingLabel": "フラップ中", "alertsUIShared.components.alertLifecycleStatusBadge.recoveredLabel": "回復済み", "alertsUIShared.components.alertLifecycleStatusBadge.untrackedLabel": "未追跡", - "alertsUIShared.hooks.useAlertDataView.useAlertDataMessage": "アラートデータビューを読み込めません", + "alertsUIShared.hooks.useAlertDataView.fetchErrorMessage": "アラートデータビューを読み込めません", "alertsUIShared.hooks.useLoadRuleTypesQuery.unableToLoadRuleTypesMessage": "ルールタイプを読み込めません", "alertsUIShared.hooks.useRuleAADFields.errorMessage": "ルールタイプごとにアラートフィールドを読み込めません", "alertsUIShared.maintenanceWindowCallout.fetchError": "保守時間枠がアクティブであるかどうかを確認できませんでした", @@ -2402,6 +2402,11 @@ "discover.embeddable.search.displayName": "検索", "discover.errorCalloutShowErrorMessage": "詳細を表示", "discover.esqlMode.selectedColumnsCallout": "{esqlQueryColumnsNumber}フィールド中{selectedColumnsNumber}フィールドを表示中です。利用可能なフィールドリストからさらに追加します。", + "discover.esqlToDataViewTransitionModal.closeButtonLabel": "保存せずに切り替え", + "discover.esqlToDataViewTransitionModal.dismissButtonLabel": "次回以降この警告を表示しない", + "discover.esqlToDataViewTransitionModal.saveButtonLabel": "保存して切り替え", + "discover.esqlToDataViewTransitionModal.title": "クエリは削除されます", + "discover.esqlToDataviewTransitionModalBody": "データビューを切り替えると、現在のES|QLクエリが削除されます。この検索を保存すると、作業内容が失われないことが保証されます。", "discover.fieldChooser.availableFieldsTooltip": "フィールドをテーブルに表示できます。", "discover.fieldChooser.discoverField.addFieldTooltip": "フィールドを列として追加", "discover.fieldChooser.discoverField.removeFieldTooltip": "フィールドを表から削除", @@ -5140,10 +5145,7 @@ "kbn-esql-validation-autocomplete.esql.validation.expectedConstantValue": "[{fn}]の引数は定数でなければなりません。[{given}]が渡されました", "kbn-esql-validation-autocomplete.esql.validation.metadataBracketsDeprecation": "FROM METADATA宣言から角括弧[]を削除する必要があります", "kbn-esql-validation-autocomplete.esql.validation.missingFunction": "不明な関数[{name}]", - "kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues": "[STATS]では集計値と非集計値を結合できません。[{expression}]が見つかりました", "kbn-esql-validation-autocomplete.esql.validation.noNestedArgumentSupport": "集計関数のパラメーターは属性、リテラル、または非集計関数でなければなりません。タイプ[{argType}]の[{name}]が見つかりました", - "kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction": "[STATS]では1つ以上の集計関数が必要です。[{expression}]が見つかりました", - "kbn-esql-validation-autocomplete.esql.validation.statsNoArguments": "[STATS]では1つ以上の集計またはグループ式が必要です", "kbn-esql-validation-autocomplete.esql.validation.typeOverwrite": "{fieldType}型の列[{field}]が新しい型の{newType}として上書きされました", "kbn-esql-validation-autocomplete.esql.validation.unknowAggregateFunction": "集計関数またはグループが想定されていますが、[{type}]型の[{value}]が渡されました", "kbn-esql-validation-autocomplete.esql.validation.unknownColumn": "不明な列[{name}]", @@ -7195,11 +7197,6 @@ "unifiedSearch.query.queryBar.indexPattern.findFilterSet": "クエリを検索", "unifiedSearch.query.queryBar.indexPattern.manageFieldButton": "このデータビューを管理", "unifiedSearch.query.queryBar.indexPattern.temporaryDataviewLabel": "一時", - "discover.esqlToDataviewTransitionModalBody": "データビューを切り替えると、現在のES|QLクエリが削除されます。この検索を保存すると、作業内容が失われないことが保証されます。", - "discover.esqlToDataViewTransitionModal.closeButtonLabel": "保存せずに切り替え", - "discover.esqlToDataViewTransitionModal.dismissButtonLabel": "次回以降この警告を表示しない", - "discover.esqlToDataViewTransitionModal.saveButtonLabel": "保存して切り替え", - "discover.esqlToDataViewTransitionModal.title": "クエリは削除されます", "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "ドキュメント", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "今後表示しない", @@ -13170,7 +13167,6 @@ "xpack.csp.emptyState.resetFiltersButton": "フィルターをリセット", "xpack.csp.emptyState.title": "検索条件と一致する結果がありません。", "xpack.csp.enableBenchmarkRuleButton": "ルールを有効にする", - "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "{total}件中{pageStart}-{pageEnd}件の{type}を表示しています", "xpack.csp.findings.distributionBar.totalFailedLabel": "失敗した調査結果", "xpack.csp.findings.distributionBar.totalPassedLabel": "合格した調査結果", "xpack.csp.findings.errorCallout.pageSearchErrorTitle": "検索結果の取得中にエラーが発生しました", @@ -13209,9 +13205,6 @@ "xpack.csp.findings.gcpIntegration.gcpInputText.credentialFileText": "サブスクライブに使用される資格情報とキーを含むJSONファイルへのパス", "xpack.csp.findings.gcpIntegration.gcpInputText.credentialJSONText": "サブスクライブに使用される資格情報とキーを含むJSON blob", "xpack.csp.findings.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "資格情報", - "xpack.csp.findings.groupBySelector.groupByLabel": "グループ分けの条件", - "xpack.csp.findings.groupBySelector.groupByNoneLabel": "なし", - "xpack.csp.findings.groupBySelector.groupByResourceIdLabel": "リソース", "xpack.csp.findings.grouping.cloudAccount.nullGroupTitle": "クラウドアカウントなし", "xpack.csp.findings.grouping.default.nullGroupTitle": "グループ分けなし", "xpack.csp.findings.grouping.kubernetes.nullGroupTitle": "Kubernetesクラスターなし", @@ -13400,9 +13393,6 @@ "xpack.csp.vulnerabilities.grouping.nullGroupUnit": "脆弱性", "xpack.csp.vulnerabilities.grouping.resource.nullGroupTitle": "リソースなし", "xpack.csp.vulnerabilities.grouping.severity": "深刻度", - "xpack.csp.vulnerabilities.searchBar.placeholder": "脆弱性を検索(例:vulnerability.severity :\"CRITICAL\")", - "xpack.csp.vulnerabilities.table.filterIn": "フィルタリング", - "xpack.csp.vulnerabilities.table.filterOut": "除外", "xpack.csp.vulnerabilities.unit": "{totalCount, plural, other {脆弱性}}", "xpack.csp.vulnerabilities.vulnerabilitiesFindingFlyout.flyoutDescriptionList.packageTitle": "パッケージ", "xpack.csp.vulnerabilities.vulnerabilitiesFindingFlyout.flyoutDescriptionList.resourceId": "リソースID", @@ -13427,8 +13417,6 @@ "xpack.csp.vulnerabilityDashboard.trendGraphChart.accountsDropDown.prepend.accountsTitle": "アカウント", "xpack.csp.vulnerabilityDashboard.trendGraphChart.trendBySeverityTitle": "重要度別傾向", "xpack.csp.vulnerabilityDashboard.viewAllButton.buttonTitle": "すべて表示", - "xpack.csp.vulnerabilityTable.column.sortAscending": "低 -> 重大", - "xpack.csp.vulnerabilityTable.column.sortDescending": "重大 -> 低", "xpack.csp.vulnerabilityTable.panel.buttonText": "すべての脆弱性を表示", "xpack.csp.vulnMgmtIntegration.awsOption.nameTitle": "Amazon Web Services", "xpack.csp.vulnMgmtIntegration.azureOption.nameTitle": "Azure", @@ -22264,7 +22252,6 @@ "xpack.infra.waffle.maxGroupByTooltip": "一度に選択できるグループは 2 つのみです", "xpack.infra.waffle.metriclabel": "メトリック", "xpack.infra.waffle.metricOptions.countText": "カウント", - "xpack.infra.waffle.metricOptions.cpuUsageText": "CPU 使用状況", "xpack.infra.waffle.metricOptions.diskIOReadBytes": "ディスク読み取り", "xpack.infra.waffle.metricOptions.diskIOWriteBytes": "ディスク書き込み", "xpack.infra.waffle.metricOptions.hostLogRateText": "ログレート", @@ -24251,13 +24238,8 @@ "xpack.lists.services.items.fileUploadFromFileSystem": "ファイルは{fileName}のファイルシステムからアップロードされました", "xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.available": "スタックトレースがあります", "xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.notAvailable": "スタックトレースがありません", - "xpack.logsExplorer.dataTable.header.actions.tooltip.expand": "ログの詳細を展開", - "xpack.logsExplorer.dataTable.header.actions.tooltip.paragraph": "次のようなアクショナブルな情報を提供するフィールド:", - "xpack.logsExplorer.dataTable.header.actions.tooltip.stacktrace": "次に基づいて使用可能なスタックトレースにアクセス:", "xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "ドキュメントの{logLevel}と{message}フィールドを表示します。", "xpack.logsExplorer.dataTable.header.content.tooltip.paragraph2": "メッセージフィールドが空のときには、次のいずれかが表示されます。", - "xpack.logsExplorer.dataTable.header.popover.actions": "アクション", - "xpack.logsExplorer.dataTable.header.popover.actions.lowercase": "アクション", "xpack.logsExplorer.dataTable.header.popover.content": "コンテンツ", "xpack.logsExplorer.dataTable.header.popover.resource": "リソース", "xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "次のようなドキュメントのソースに関する情報を提供するフィールド:", @@ -28209,7 +28191,6 @@ "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "モデルにはパイプラインが関連付けられています", "xpack.ml.trainedModels.modelsList.downloadFailed": "\"{modelId}\"をダウンロードできませんでした", "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "ダウンロードステータスを確認できませんでした", - "xpack.ml.trainedModels.modelsList.downloadSuccess": "\"{modelId}\"モデルのダウンロードが正常に開始しました。", "xpack.ml.trainedModels.modelsList.e5Title": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "E5(bidirEctional Encoder rEpresentationsからのEmbEddings)、inux-x86_64向けに最適化", @@ -28267,7 +28248,6 @@ "xpack.ml.trainedModels.modelsList.pipelines.processorStats.timePerDocHeader": "ドキュメントごとの時間", "xpack.ml.trainedModels.modelsList.pipelines.processorStats.typeHeader": "プロセッサータイプ", "xpack.ml.trainedModels.modelsList.recommendedDownloadContent": "クラスターのハードウェア構成に応じた推奨モデルバージョン", - "xpack.ml.trainedModels.modelsList.recommendedDownloadLabel": "(推奨)", "xpack.ml.trainedModels.modelsList.selectableMessage": "モデルを選択", "xpack.ml.trainedModels.modelsList.selectedModelsMessage": "{modelsCount, plural, other {#個のモデル}}が選択されました", "xpack.ml.trainedModels.modelsList.startDeployment.cancelButton": "キャンセル", @@ -37376,9 +37356,6 @@ "xpack.securitySolution.ruleExceptions.allExceptionItems.noSearchResultsPromptTitle": "検索条件と一致する結果がありません。", "xpack.securitySolution.ruleExceptions.allExceptionItems.paginationAriaLabel": "例外アイテムの表のページ制御", "xpack.securitySolution.ruleExceptions.allExceptionItems.searchPlaceholder": "シンプルなクエリ構文(name:\"my list\"など)を使用して例外をフィルタリング", - "xpack.securitySolution.ruleExceptions.editException.cancel": "キャンセル", - "xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle": "エンドポイント例外の編集", - "xpack.securitySolution.ruleExceptions.editException.editExceptionTitle": "ルール例外を編集", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastErrorTitle": "例外の更新エラー", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessText": "{numItems, plural, other {例外}} - {exceptionItemName} - {numItems, plural, other {が}}更新されました。", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle": "ルール例外が更新されました", @@ -42802,7 +42779,6 @@ "xpack.triggersActionsUI.updateApiKeyConfirmModal.failureMessage": "API {idsToUpdate, plural, other {キー}}を更新できませんでした", "xpack.triggersActionsUI.updateApiKeyConfirmModal.title": "APIキーの更新", "xpack.triggersActionsUI.urlSyncedAlertsSearchBar.invalidQueryTitle": "無効なクエリ文字列", - "xpack.triggersActionsUI.useAlertDataView.useAlertDataMessage": "アラートデータビューを読み込めません", "xpack.triggersActionsUI.useRuleAADFields.errorMessage": "ルールタイプごとにアラートフィールドを読み込めません", "xpack.upgradeAssistant.app.deniedPrivilegeDescription": "アップグレードアシスタントを使用して、廃止予定の問題を解決するには、すべてのKibanaスペースを管理するためのアクセス権が必要です。", "xpack.upgradeAssistant.app.deniedPrivilegeTitle": "Kibana管理者ロールが必要です", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e1b7758a38725..e314413a565e8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -104,7 +104,7 @@ "alertsUIShared.components.alertLifecycleStatusBadge.flappingLabel": "摆动", "alertsUIShared.components.alertLifecycleStatusBadge.recoveredLabel": "已恢复", "alertsUIShared.components.alertLifecycleStatusBadge.untrackedLabel": "已取消跟踪", - "alertsUIShared.hooks.useAlertDataView.useAlertDataMessage": "无法加载告警数据视图", + "alertsUIShared.hooks.useAlertDataView.fetchErrorMessage": "无法加载告警数据视图", "alertsUIShared.hooks.useLoadRuleTypesQuery.unableToLoadRuleTypesMessage": "无法加载规则类型", "alertsUIShared.hooks.useRuleAADFields.errorMessage": "无法按规则类型加载告警字段", "alertsUIShared.maintenanceWindowCallout.fetchError": "无法检查维护窗口是否处于活动状态", @@ -2408,6 +2408,11 @@ "discover.embeddable.search.displayName": "搜索", "discover.errorCalloutShowErrorMessage": "查看详情", "discover.esqlMode.selectedColumnsCallout": "正在显示 {selectedColumnsNumber} 个字段,共 {esqlQueryColumnsNumber} 个。从可用字段列表中添加更多字段。", + "discover.esqlToDataViewTransitionModal.closeButtonLabel": "切换而不保存", + "discover.esqlToDataViewTransitionModal.dismissButtonLabel": "不再显示此警告", + "discover.esqlToDataViewTransitionModal.saveButtonLabel": "保存并切换", + "discover.esqlToDataViewTransitionModal.title": "将移除您的查询", + "discover.esqlToDataviewTransitionModalBody": "切换数据视图会移除当前的 ES|QL 查询。保存此搜索以确保不会丢失工作。", "discover.fieldChooser.availableFieldsTooltip": "适用于在表中显示的字段。", "discover.fieldChooser.discoverField.addFieldTooltip": "将字段添加为列", "discover.fieldChooser.discoverField.removeFieldTooltip": "从表中移除字段", @@ -5165,10 +5170,7 @@ "kbn-esql-validation-autocomplete.esql.validation.expectedConstantValue": "[{fn}] 的参数必须为常数,收到的是 [{given}]", "kbn-esql-validation-autocomplete.esql.validation.metadataBracketsDeprecation": "需要从 FROM METADATA 声明中移除方括号“[]”", "kbn-esql-validation-autocomplete.esql.validation.missingFunction": "未知函数 [{name}]", - "kbn-esql-validation-autocomplete.esql.validation.noCombinationOfAggAndNonAggValues": "无法在 [STATS] 中组合聚合与非聚合值,找到了 [{expression}]", "kbn-esql-validation-autocomplete.esql.validation.noNestedArgumentSupport": "聚合函数的参数必须为属性、文本或非聚合函数;找到了 [{argType}] 类型的 [{name}]", - "kbn-esql-validation-autocomplete.esql.validation.statsNoAggFunction": "[STATS] 中至少需要一个聚合函数,找到了 [{expression}]", - "kbn-esql-validation-autocomplete.esql.validation.statsNoArguments": "[STATS] 中至少需要一个聚合或分组表达式", "kbn-esql-validation-autocomplete.esql.validation.typeOverwrite": "类型为 {fieldType} 的列 [{field}] 已重写为新类型:{newType}", "kbn-esql-validation-autocomplete.esql.validation.unknowAggregateFunction": "应为聚合函数或组,但收到的是 [{type}] 类型的 [{value}]", "kbn-esql-validation-autocomplete.esql.validation.unknownColumn": "未知列 [{name}]", @@ -6647,7 +6649,6 @@ "textBasedEditor.query.textBasedLanguagesEditor.functionsDocumentationESQLDescription": "ROW、EVAL 和 WHERE 支持的函数。", "textBasedEditor.query.textBasedLanguagesEditor.lineCount": "{count} {count, plural, other {行}}", "textBasedEditor.query.textBasedLanguagesEditor.lineNumber": "第 {lineNumber} 行", - "": "压缩查询编辑器", "textBasedEditor.query.textBasedLanguagesEditor.operators": "运算符", "textBasedEditor.query.textBasedLanguagesEditor.operatorsDocumentationESQLDescription": "ES|QL 支持以下运算符:", "textBasedEditor.query.textBasedLanguagesEditor.processingCommands": "处理命令", @@ -7232,11 +7233,6 @@ "unifiedSearch.query.queryBar.indexPattern.findFilterSet": "查找查询", "unifiedSearch.query.queryBar.indexPattern.manageFieldButton": "管理此数据视图", "unifiedSearch.query.queryBar.indexPattern.temporaryDataviewLabel": "临时", - "discover.esqlToDataviewTransitionModalBody": "切换数据视图会移除当前的 ES|QL 查询。保存此搜索以确保不会丢失工作。", - "discover.esqlToDataViewTransitionModal.closeButtonLabel": "切换而不保存", - "discover.esqlToDataViewTransitionModal.dismissButtonLabel": "不再显示此警告", - "discover.esqlToDataViewTransitionModal.saveButtonLabel": "保存并切换", - "discover.esqlToDataViewTransitionModal.title": "将移除您的查询", "unifiedSearch.query.queryBar.kqlLanguageName": "KQL", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoDocLinkText": "文档", "unifiedSearch.query.queryBar.KQLNestedQuerySyntaxInfoOptOutText": "不再显示", @@ -13231,7 +13227,6 @@ "xpack.csp.emptyState.resetFiltersButton": "重置筛选", "xpack.csp.emptyState.title": "没有任何结果匹配您的搜索条件", "xpack.csp.enableBenchmarkRuleButton": "启用规则", - "xpack.csp.findings.distributionBar.showingPageOfTotalLabel": "正在显示第 {pageStart}-{pageEnd} 个(共 {total} 个){type}", "xpack.csp.findings.distributionBar.totalFailedLabel": "失败的结果", "xpack.csp.findings.distributionBar.totalPassedLabel": "通过的结果", "xpack.csp.findings.errorCallout.pageSearchErrorTitle": "检索搜索结果时遇到问题", @@ -13270,9 +13265,6 @@ "xpack.csp.findings.gcpIntegration.gcpInputText.credentialFileText": "包含用于订阅的凭据和密钥的 JSON 文件的路径", "xpack.csp.findings.gcpIntegration.gcpInputText.credentialJSONText": "包含用于订阅的凭据和密钥的 JSON Blob", "xpack.csp.findings.gcpIntegration.gcpInputText.credentialSelectBoxTitle": "凭据", - "xpack.csp.findings.groupBySelector.groupByLabel": "分组依据", - "xpack.csp.findings.groupBySelector.groupByNoneLabel": "无", - "xpack.csp.findings.groupBySelector.groupByResourceIdLabel": "资源", "xpack.csp.findings.grouping.cloudAccount.nullGroupTitle": "无云帐户", "xpack.csp.findings.grouping.default.nullGroupTitle": "无分组", "xpack.csp.findings.grouping.kubernetes.nullGroupTitle": "无 Kubernetes 集群", @@ -13462,9 +13454,6 @@ "xpack.csp.vulnerabilities.grouping.nullGroupUnit": "漏洞", "xpack.csp.vulnerabilities.grouping.resource.nullGroupTitle": "无资源", "xpack.csp.vulnerabilities.grouping.severity": "严重性", - "xpack.csp.vulnerabilities.searchBar.placeholder": "搜索漏洞(例如,vulnerability.severity:“CRITICAL”)", - "xpack.csp.vulnerabilities.table.filterIn": "筛选范围", - "xpack.csp.vulnerabilities.table.filterOut": "筛除", "xpack.csp.vulnerabilities.unit": "{totalCount, plural, other {个漏洞}}", "xpack.csp.vulnerabilities.vulnerabilitiesFindingFlyout.flyoutDescriptionList.packageTitle": "软件包", "xpack.csp.vulnerabilities.vulnerabilitiesFindingFlyout.flyoutDescriptionList.resourceId": "资源 ID", @@ -13489,8 +13478,6 @@ "xpack.csp.vulnerabilityDashboard.trendGraphChart.accountsDropDown.prepend.accountsTitle": "帐户", "xpack.csp.vulnerabilityDashboard.trendGraphChart.trendBySeverityTitle": "趋势(按严重性)", "xpack.csp.vulnerabilityDashboard.viewAllButton.buttonTitle": "查看全部", - "xpack.csp.vulnerabilityTable.column.sortAscending": "低 -> 严重", - "xpack.csp.vulnerabilityTable.column.sortDescending": "严重 -> 低", "xpack.csp.vulnerabilityTable.panel.buttonText": "查看所有漏洞", "xpack.csp.vulnMgmtIntegration.awsOption.nameTitle": "Amazon Web Services", "xpack.csp.vulnMgmtIntegration.azureOption.nameTitle": "Azure", @@ -22368,7 +22355,6 @@ "xpack.infra.waffle.maxGroupByTooltip": "一次只能选择两个分组", "xpack.infra.waffle.metriclabel": "指标", "xpack.infra.waffle.metricOptions.countText": "计数", - "xpack.infra.waffle.metricOptions.cpuUsageText": "CPU 使用", "xpack.infra.waffle.metricOptions.diskIOReadBytes": "磁盘读取数", "xpack.infra.waffle.metricOptions.diskIOWriteBytes": "磁盘写入数", "xpack.infra.waffle.metricOptions.hostLogRateText": "日志速率", @@ -24361,13 +24347,8 @@ "xpack.lists.services.items.fileUploadFromFileSystem": "从 {fileName} 的文件系统上传的文件", "xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.available": "堆栈跟踪可用", "xpack.logsExplorer.dataTable.controlColumn.actions.button.stacktrace.notAvailable": "堆栈跟踪不可用", - "xpack.logsExplorer.dataTable.header.actions.tooltip.expand": "展开日志详情", - "xpack.logsExplorer.dataTable.header.actions.tooltip.paragraph": "提供可操作信息的字段,例如:", - "xpack.logsExplorer.dataTable.header.actions.tooltip.stacktrace": "基于以下项访问可用堆栈跟踪:", "xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "显示该文档的 {logLevel} 和 {message} 字段。", "xpack.logsExplorer.dataTable.header.content.tooltip.paragraph2": "消息字段为空时,将显示以下项之一:", - "xpack.logsExplorer.dataTable.header.popover.actions": "操作", - "xpack.logsExplorer.dataTable.header.popover.actions.lowercase": "操作", "xpack.logsExplorer.dataTable.header.popover.content": "内容", "xpack.logsExplorer.dataTable.header.popover.resource": "资源", "xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "提供有关文档来源信息的字段,例如:", @@ -28344,7 +28325,6 @@ "xpack.ml.trainedModels.modelsList.disableSelectableMessage": "模型有关联的管道", "xpack.ml.trainedModels.modelsList.downloadFailed": "无法下载“{modelId}”", "xpack.ml.trainedModels.modelsList.downloadStatusCheckErrorMessage": "无法检查下载状态", - "xpack.ml.trainedModels.modelsList.downloadSuccess": "已成功启动“{modelId}”模型下载。", "xpack.ml.trainedModels.modelsList.e5Title": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1Description": "E5 (EmbEddings from bidirEctional Encoder rEpresentations)", "xpack.ml.trainedModels.modelsList.e5v1x86Description": "针对 linux-x86_64 进行了优化的 E5 (EmbEddings from bidirEctional Encoder rEpresentations)", @@ -28402,7 +28382,6 @@ "xpack.ml.trainedModels.modelsList.pipelines.processorStats.timePerDocHeader": "每个文档的时间", "xpack.ml.trainedModels.modelsList.pipelines.processorStats.typeHeader": "处理器类型", "xpack.ml.trainedModels.modelsList.recommendedDownloadContent": "为您集群的硬件配置推荐的模型版本", - "xpack.ml.trainedModels.modelsList.recommendedDownloadLabel": "(推荐)", "xpack.ml.trainedModels.modelsList.selectableMessage": "选择模型", "xpack.ml.trainedModels.modelsList.selectedModelsMessage": "{modelsCount, plural, other {# 个模型}}已选择", "xpack.ml.trainedModels.modelsList.startDeployment.cancelButton": "取消", @@ -37547,9 +37526,6 @@ "xpack.securitySolution.ruleExceptions.allExceptionItems.noSearchResultsPromptTitle": "没有任何结果匹配您的搜索条件", "xpack.securitySolution.ruleExceptions.allExceptionItems.paginationAriaLabel": "例外项表分页", "xpack.securitySolution.ruleExceptions.allExceptionItems.searchPlaceholder": "使用简单的查询语法筛选例外,例如 name:\"my list\"", - "xpack.securitySolution.ruleExceptions.editException.cancel": "取消", - "xpack.securitySolution.ruleExceptions.editException.editEndpointExceptionTitle": "编辑终端例外", - "xpack.securitySolution.ruleExceptions.editException.editExceptionTitle": "编辑规则例外", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastErrorTitle": "更新例外时出错", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessText": "{numItems, plural, other {例外}} - {exceptionItemName} - {numItems, plural, other {已}}更新。", "xpack.securitySolution.ruleExceptions.editException.editRuleExceptionToastSuccessTitle": "已更新规则例外", @@ -42993,7 +42969,6 @@ "xpack.triggersActionsUI.updateApiKeyConfirmModal.failureMessage": "无法更新 API {idsToUpdate, plural, other {密钥}}", "xpack.triggersActionsUI.updateApiKeyConfirmModal.title": "更新 API 密钥", "xpack.triggersActionsUI.urlSyncedAlertsSearchBar.invalidQueryTitle": "字符串查询无效", - "xpack.triggersActionsUI.useAlertDataView.useAlertDataMessage": "无法加载告警数据视图", "xpack.triggersActionsUI.useRuleAADFields.errorMessage": "无法按规则类型加载告警字段", "xpack.upgradeAssistant.app.deniedPrivilegeDescription": "要使用升级助手并解决弃用问题,必须具有管理所有 Kibana 工作区的访问权限。", "xpack.upgradeAssistant.app.deniedPrivilegeTitle": "需要 Kibana 管理员角色", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.tsx deleted file mode 100644 index b12d0454b997c..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.test.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { AlertConsumers } from '@kbn/rule-data-utils'; -import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; -import type { ValidFeatureId } from '@kbn/rule-data-utils'; -import { renderHook } from '@testing-library/react-hooks/dom'; -import { useAlertDataViews } from './use_alert_data_view'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import React from 'react'; -import { waitFor } from '@testing-library/react'; - -const mockUseKibanaReturnValue = createStartServicesMock(); - -jest.mock('@kbn/kibana-react-plugin/public', () => ({ - __esModule: true, - useKibana: jest.fn(() => ({ - services: mockUseKibanaReturnValue, - })), -})); - -jest.mock('../lib/rule_api/alert_index', () => ({ - fetchAlertIndexNames: jest.fn(), -})); - -const { fetchAlertIndexNames } = jest.requireMock('../lib/rule_api/alert_index'); - -jest.mock('../lib/rule_api/alert_fields', () => ({ - fetchAlertFields: jest.fn(), -})); -const { fetchAlertFields } = jest.requireMock('../lib/rule_api/alert_fields'); - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: false, - cacheTime: 0, - }, - }, -}); -const wrapper = ({ children }: { children: Node }) => ( - <QueryClientProvider client={queryClient}> {children} </QueryClientProvider> -); - -describe('useAlertDataView', () => { - const observabilityAlertFeatureIds: ValidFeatureId[] = [ - AlertConsumers.APM, - AlertConsumers.INFRASTRUCTURE, - AlertConsumers.LOGS, - AlertConsumers.UPTIME, - ]; - - beforeEach(() => { - fetchAlertIndexNames.mockResolvedValue([ - '.alerts-observability.uptime.alerts-*', - '.alerts-observability.metrics.alerts-*', - '.alerts-observability.logs.alerts-*', - '.alerts-observability.apm.alerts-*', - ]); - fetchAlertFields.mockResolvedValue([{ data: ' fields' }]); - }); - - afterEach(() => { - queryClient.clear(); - jest.clearAllMocks(); - }); - - it('initially is loading and does not have data', async () => { - const mockedAsyncDataView = { - loading: true, - dataview: undefined, - }; - - const { result } = renderHook(() => useAlertDataViews(observabilityAlertFeatureIds), { - wrapper, - }); - - await waitFor(() => expect(result.current).toEqual(mockedAsyncDataView)); - }); - - it('fetch index names + fields for the provided o11y featureIds', async () => { - renderHook(() => useAlertDataViews(observabilityAlertFeatureIds), { - wrapper, - }); - - await waitFor(() => expect(fetchAlertIndexNames).toHaveBeenCalledTimes(1)); - expect(fetchAlertFields).toHaveBeenCalledTimes(1); - }); - - it('only fetch index names for security featureId', async () => { - renderHook(() => useAlertDataViews([AlertConsumers.SIEM]), { - wrapper, - }); - - await waitFor(() => expect(fetchAlertIndexNames).toHaveBeenCalledTimes(1)); - expect(fetchAlertFields).toHaveBeenCalledTimes(0); - }); - - it('Do not fetch anything if security and o11y featureIds are mixed together', async () => { - const { result } = renderHook( - () => useAlertDataViews([AlertConsumers.SIEM, AlertConsumers.LOGS]), - { - wrapper, - } - ); - - await waitFor(() => - expect(result.current).toEqual({ - loading: false, - dataview: undefined, - }) - ); - expect(fetchAlertIndexNames).toHaveBeenCalledTimes(0); - expect(fetchAlertFields).toHaveBeenCalledTimes(0); - }); - - it('if fetch throws error return no data', async () => { - fetchAlertIndexNames.mockRejectedValue('error'); - - const { result } = renderHook(() => useAlertDataViews(observabilityAlertFeatureIds), { - wrapper, - }); - - await waitFor(() => - expect(result.current).toEqual({ - loading: false, - dataview: undefined, - }) - ); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts deleted file mode 100644 index 322bab6c88301..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_alert_data_view.ts +++ /dev/null @@ -1,162 +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 { DataView } from '@kbn/data-views-plugin/common'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; -import { useEffect, useMemo, useState } from 'react'; -import { useQuery } from '@tanstack/react-query'; -import { TriggersAndActionsUiServices } from '../..'; -import { fetchAlertIndexNames } from '../lib/rule_api/alert_index'; -import { fetchAlertFields } from '../lib/rule_api/alert_fields'; - -export interface UserAlertDataViews { - dataViews?: DataView[]; - loading: boolean; -} - -export function useAlertDataViews(featureIds: ValidFeatureId[]): UserAlertDataViews { - const { - http, - data: dataService, - notifications: { toasts }, - } = useKibana<TriggersAndActionsUiServices>().services; - const [dataViews, setDataViews] = useState<DataView[] | undefined>(undefined); - const features = featureIds.sort().join(','); - const isOnlySecurity = featureIds.length === 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasSecurityAndO11yFeatureIds = - featureIds.length > 1 && featureIds.includes(AlertConsumers.SIEM); - - const hasNoSecuritySolution = - featureIds.length > 0 && !isOnlySecurity && !hasSecurityAndO11yFeatureIds; - - const queryIndexNameFn = () => { - return fetchAlertIndexNames({ http, features }); - }; - - const queryAlertFieldsFn = () => { - return fetchAlertFields({ http, featureIds }); - }; - - const onErrorFn = () => { - toasts.addDanger( - i18n.translate('xpack.triggersActionsUI.useAlertDataView.useAlertDataMessage', { - defaultMessage: 'Unable to load alert data view', - }) - ); - }; - - const { - data: indexNames, - isSuccess: isIndexNameSuccess, - isInitialLoading: isIndexNameInitialLoading, - isLoading: isIndexNameLoading, - } = useQuery({ - queryKey: ['loadAlertIndexNames', features], - queryFn: queryIndexNameFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - enabled: featureIds.length > 0 && !hasSecurityAndO11yFeatureIds, - }); - - const { - data: alertFields, - isSuccess: isAlertFieldsSuccess, - isInitialLoading: isAlertFieldsInitialLoading, - isLoading: isAlertFieldsLoading, - } = useQuery({ - queryKey: ['loadAlertFields', features], - queryFn: queryAlertFieldsFn, - onError: onErrorFn, - refetchOnWindowFocus: false, - enabled: hasNoSecuritySolution, - }); - - useEffect(() => { - return () => { - dataViews?.map((dv) => { - dataService.dataViews.clearInstanceCache(dv.id); - }); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dataViews]); - - // FUTURE ENGINEER this useEffect is for security solution user since - // we are using the user privilege to access the security alert index - useEffect(() => { - async function createDataView() { - const localDataview = await dataService.dataViews.create({ - title: (indexNames ?? []).join(','), - allowNoIndex: true, - }); - setDataViews([localDataview]); - } - - if (isOnlySecurity && isIndexNameSuccess) { - createDataView(); - } - }, [dataService.dataViews, indexNames, isIndexNameSuccess, isOnlySecurity]); - - // FUTURE ENGINEER this useEffect is for o11y and stack solution user since - // we are using the kibana user privilege to access the alert index - useEffect(() => { - if ( - indexNames && - alertFields && - !isOnlySecurity && - isAlertFieldsSuccess && - isIndexNameSuccess - ) { - setDataViews([ - { - title: (indexNames ?? []).join(','), - fieldFormatMap: {}, - fields: (alertFields ?? [])?.map((field) => { - return { - ...field, - ...(field.esTypes && field.esTypes.includes('flattened') ? { type: 'string' } : {}), - }; - }), - }, - ] as unknown as DataView[]); - } - }, [ - alertFields, - dataService.dataViews, - indexNames, - isIndexNameSuccess, - isOnlySecurity, - isAlertFieldsSuccess, - ]); - - return useMemo( - () => ({ - dataViews, - loading: - featureIds.length === 0 || hasSecurityAndO11yFeatureIds - ? false - : isOnlySecurity - ? isIndexNameInitialLoading || isIndexNameLoading - : isIndexNameInitialLoading || - isIndexNameLoading || - isAlertFieldsInitialLoading || - isAlertFieldsLoading, - }), - [ - dataViews, - featureIds.length, - hasSecurityAndO11yFeatureIds, - isOnlySecurity, - isIndexNameInitialLoading, - isIndexNameLoading, - isAlertFieldsInitialLoading, - isAlertFieldsLoading, - ] - ); -} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 2f4f0492ad73a..3896e5d0e938a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -9,25 +9,26 @@ import React, { useCallback, useMemo, useState } from 'react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { compareFilters, Query, TimeRange } from '@kbn/es-query'; import { SuggestionsAbstraction } from '@kbn/unified-search-plugin/public/typeahead/suggestions_component'; -import { AlertConsumers } from '@kbn/rule-data-utils'; +import { AlertConsumers, ValidFeatureId } from '@kbn/rule-data-utils'; import { EuiContextMenuPanelDescriptor, EuiContextMenuPanelItemDescriptor } from '@elastic/eui'; +import { useAlertsDataView } from '@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'; import { isQuickFiltersGroup, QuickFiltersMenuItem } from './quick_filters'; import { NO_INDEX_PATTERNS } from './constants'; import { SEARCH_BAR_PLACEHOLDER } from './translations'; import { AlertsSearchBarProps, QueryLanguageType } from './types'; -import { useAlertDataViews } from '../../hooks/use_alert_data_view'; import { TriggersAndActionsUiServices } from '../../..'; import { useRuleAADFields } from '../../hooks/use_rule_aad_fields'; import { useLoadRuleTypesQuery } from '../../hooks/use_load_rule_types_query'; const SA_ALERTS = { type: 'alerts', fields: {} } as SuggestionsAbstraction; +const EMPTY_FEATURE_IDS: ValidFeatureId[] = []; // TODO Share buildEsQuery to be used between AlertsSearchBar and AlertsStateTable component https://github.com/elastic/kibana/issues/144615 // Also TODO: Replace all references to this component with the one from alerts-ui-shared export function AlertsSearchBar({ appName, disableQueryLanguageSwitcher = false, - featureIds, + featureIds = EMPTY_FEATURE_IDS, ruleTypeId, query, filters, @@ -46,17 +47,32 @@ export function AlertsSearchBar({ ...props }: AlertsSearchBarProps) { const { + http, + dataViews: dataViewsService, + notifications: { toasts }, unifiedSearch: { ui: { SearchBar }, }, } = useKibana<TriggersAndActionsUiServices>().services; const [queryLanguage, setQueryLanguage] = useState<QueryLanguageType>('kuery'); - const { dataViews, loading } = useAlertDataViews(featureIds ?? []); + const { dataView } = useAlertsDataView({ + featureIds, + http, + dataViewsService, + toasts, + }); const { aadFields, loading: fieldsLoading } = useRuleAADFields(ruleTypeId); - const indexPatterns = - ruleTypeId && aadFields?.length ? [{ title: ruleTypeId, fields: aadFields }] : dataViews; + const indexPatterns = useMemo(() => { + if (ruleTypeId && aadFields?.length) { + return [{ title: ruleTypeId, fields: aadFields }]; + } + if (dataView) { + return [dataView]; + } + return null; + }, [aadFields, dataView, ruleTypeId]); const ruleType = useLoadRuleTypesQuery({ filteredRuleTypes: ruleTypeId !== undefined ? [ruleTypeId] : [], @@ -157,7 +173,7 @@ export function AlertsSearchBar({ appName={appName} disableQueryLanguageSwitcher={disableQueryLanguageSwitcher} // @ts-expect-error - DataView fields prop and SearchBar indexPatterns props are overly broad - indexPatterns={loading || fieldsLoading ? NO_INDEX_PATTERNS : indexPatterns} + indexPatterns={!indexPatterns || fieldsLoading ? NO_INDEX_PATTERNS : indexPatterns} placeholder={placeholder} query={{ query: query ?? '', language: queryLanguage }} filters={filters} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx index 09de049798a12..84b6be1a91560 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.test.tsx @@ -40,7 +40,7 @@ import { getMaintenanceWindowMockMap } from './maintenance_windows/index.mock'; import { AlertTableConfigRegistry } from '../../alert_table_config_registry'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { fetchAlertsFields } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_fields'; -import { useSearchAlertsQuery } from '@kbn/alerts-ui-shared/src/common/hooks'; +import { useSearchAlertsQuery } from '@kbn/alerts-ui-shared/src/common/hooks/use_search_alerts_query'; jest.mock('@kbn/kibana-utils-plugin/public'); jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_search_alerts_query'); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx index 2ce8ed3e28e6e..c2b147f2821e8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx @@ -30,7 +30,7 @@ import type { SortCombinations, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { QueryClientProvider } from '@tanstack/react-query'; -import { useSearchAlertsQuery } from '@kbn/alerts-ui-shared/src/common/hooks'; +import { useSearchAlertsQuery } from '@kbn/alerts-ui-shared/src/common/hooks/use_search_alerts_query'; import { DEFAULT_ALERTS_PAGE_SIZE } from '@kbn/alerts-ui-shared/src/common/constants'; import { AlertsQueryContext } from '@kbn/alerts-ui-shared/src/common/contexts/alerts_query_context'; import deepEqual from 'fast-deep-equal'; @@ -296,7 +296,7 @@ const AlertsTableStateWithQueryProvider = memo( storage, id, defaultColumns: columnConfigByClient, - browserFields: propBrowserFields, + alertsFields: propBrowserFields, }); const [queryParams, setQueryParams] = useState({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.test.tsx index 97d7ba7c2545e..ced47444f3a8f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.test.tsx @@ -116,8 +116,9 @@ describe('useColumns', () => { ]; beforeEach(() => { - setItemStorageMock.mockClear(); + jest.clearAllMocks(); storage = { current: new Storage(mockStorage) }; + queryClient.clear(); }); test('onColumnResize', async () => { @@ -152,6 +153,28 @@ describe('useColumns', () => { }); }); + test("does not fetch alerts fields if they're overridden through the alertsFields prop", () => { + const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns); + const alertsFields = { + testField: { name: 'testField', type: 'string', searchable: true, aggregatable: true }, + }; + const { result } = renderHook<UseColumnsArgs, UseColumnsResp>( + () => + useColumns({ + alertsFields, + defaultColumns, + featureIds, + id, + storageAlertsTable: localStorageAlertsTable, + storage, + }), + { wrapper } + ); + + expect(mockFetchAlertsFields).not.toHaveBeenCalled(); + expect(result.current.browserFields).toEqual(alertsFields); + }); + describe('visibleColumns', () => { test('hide all columns with onChangeVisibleColumns', async () => { const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns); @@ -204,7 +227,7 @@ describe('useColumns', () => { expect(result.current.columns).toEqual(defaultColumns); }); - test('should populate visiblecolumns correctly', async () => { + test('should populate visibleColumns correctly', async () => { const localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(defaultColumns); const { result } = renderHook<UseColumnsArgs, UseColumnsResp>( () => @@ -221,7 +244,7 @@ describe('useColumns', () => { expect(result.current.visibleColumns).toMatchObject(defaultColumns.map((col) => col.id)); }); - test('should change visiblecolumns if provided defaultColumns change', async () => { + test('should change visibleColumns if provided defaultColumns change', async () => { let localDefaultColumns = [...defaultColumns]; let localStorageAlertsTable = getStorageAlertsTableByDefaultColumns(localDefaultColumns); const { result, rerender } = renderHook<UseColumnsArgs, UseColumnsResp>( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts index a78b92b6f7599..8846470a2d263 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_columns/use_columns.ts @@ -8,7 +8,7 @@ import { EuiDataGridColumn, EuiDataGridOnColumnResizeData } from '@elastic/eui'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { BrowserField, BrowserFields } from '@kbn/alerting-types'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { AlertConsumers } from '@kbn/rule-data-utils'; import { isEmpty } from 'lodash'; import { useFetchAlertsFieldsQuery } from '@kbn/alerts-ui-shared/src/common/hooks/use_fetch_alerts_fields_query'; @@ -23,7 +23,11 @@ export interface UseColumnsArgs { storage: React.MutableRefObject<IStorageWrapper>; id: string; defaultColumns: EuiDataGridColumn[]; - browserFields?: BrowserFields; + /** + * If this is provided, it will be used to populate the columns instead of fetching the fields + * from the alerting APIs + */ + alertsFields?: BrowserFields; } export interface UseColumnsResp { @@ -137,8 +141,8 @@ const persist = ({ visibleColumns, }: { id: string; - storageAlertsTable: React.MutableRefObject<AlertsTableStorage>; - storage: React.MutableRefObject<IStorageWrapper>; + storageAlertsTable: MutableRefObject<AlertsTableStorage>; + storage: MutableRefObject<IStorageWrapper>; columns: EuiDataGridColumn[]; visibleColumns: string[]; }) => { @@ -156,25 +160,31 @@ export const useColumns = ({ storage, id, defaultColumns, - browserFields, + alertsFields, }: UseColumnsArgs): UseColumnsResp => { const { http } = useKibana().services; - const { isLoading: isBrowserFieldDataLoading, data } = useFetchAlertsFieldsQuery( + const fieldsQuery = useFetchAlertsFieldsQuery( { http, featureIds, }, { + enabled: !alertsFields, context: AlertsQueryContext, } ); + const selectedAlertsFields = useMemo<BrowserFields>( + () => alertsFields ?? fieldsQuery.data?.browserFields ?? {}, + [alertsFields, fieldsQuery.data?.browserFields] + ); + const [columns, setColumns] = useState<EuiDataGridColumn[]>(() => { let cols = storageAlertsTable.current.columns; // before restoring from storage, enrich the column data - if (browserFields && defaultColumns) { - cols = populateColumns(cols, browserFields, defaultColumns); + if (alertsFields && defaultColumns) { + cols = populateColumns(cols, alertsFields, defaultColumns); } else if (cols && cols.length === 0) { cols = defaultColumns; } @@ -206,7 +216,7 @@ export const useColumns = ({ ); useEffect(() => { - // if defaultColumns have changed, + // If defaultColumns have changed, // get the latest columns provided by client and if (didDefaultColumnChange && defaultColumnsRef.current) { defaultColumnsRef.current = defaultColumns; @@ -220,13 +230,19 @@ export const useColumns = ({ }, [didDefaultColumnChange, storageAlertsTable, defaultColumns, visibleColumns]); useEffect(() => { - if (isEmpty(data.browserFields) || isColumnsPopulated) return; + if (fieldsQuery.data) { + if (isEmpty(fieldsQuery.data.browserFields) || isColumnsPopulated) return; - const populatedColumns = populateColumns(columns, data.browserFields, defaultColumns); + const populatedColumns = populateColumns( + columns, + fieldsQuery.data.browserFields, + defaultColumns + ); - setColumnsPopulated(true); - setColumns(populatedColumns); - }, [data.browserFields, defaultColumns, isBrowserFieldDataLoading, isColumnsPopulated, columns]); + setColumnsPopulated(true); + setColumns(populatedColumns); + } + }, [defaultColumns, fieldsQuery.isLoading, isColumnsPopulated, columns, fieldsQuery.data]); const setColumnsAndSave = useCallback( (newColumns: EuiDataGridColumn[], newVisibleColumns: string[]) => { @@ -244,7 +260,7 @@ export const useColumns = ({ const onToggleColumn = useCallback( (columnId: string): void => { - const column = euiColumnFactory(columnId, data.browserFields, defaultColumns); + const column = euiColumnFactory(columnId, selectedAlertsFields, defaultColumns); const newColumns = toggleColumn({ column, @@ -260,19 +276,19 @@ export const useColumns = ({ setVisibleColumns(newVisibleColumns); setColumnsAndSave(newColumns, newVisibleColumns); }, - [data.browserFields, columns, defaultColumns, setColumnsAndSave, visibleColumns] + [selectedAlertsFields, columns, defaultColumns, setColumnsAndSave, visibleColumns] ); const onResetColumns = useCallback(() => { const populatedDefaultColumns = populateColumns( defaultColumns, - data.browserFields, + selectedAlertsFields, defaultColumns ); const newVisibleColumns = populatedDefaultColumns.map((pdc) => pdc.id); setVisibleColumns(newVisibleColumns); setColumnsAndSave(populatedDefaultColumns, newVisibleColumns); - }, [data.browserFields, defaultColumns, setColumnsAndSave]); + }, [selectedAlertsFields, defaultColumns, setColumnsAndSave]); const onColumnResize = useCallback( ({ columnId, width }: EuiDataGridOnColumnResizeData) => { @@ -294,8 +310,8 @@ export const useColumns = ({ () => ({ columns, visibleColumns, - isBrowserFieldDataLoading, - browserFields: browserFields ?? data.browserFields, + isBrowserFieldDataLoading: fieldsQuery.isLoading, + browserFields: selectedAlertsFields, onToggleColumn, onResetColumns, onChangeVisibleColumns: setColumnsByColumnIds, @@ -305,9 +321,8 @@ export const useColumns = ({ [ columns, visibleColumns, - isBrowserFieldDataLoading, - browserFields, - data.browserFields, + fieldsQuery.isLoading, + selectedAlertsFields, onToggleColumn, onResetColumns, setColumnsByColumnIds, diff --git a/x-pack/test/api_integration/apis/metrics_ui/infra.ts b/x-pack/test/api_integration/apis/metrics_ui/infra.ts index dfb6744a94583..641f61c9126fe 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/infra.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/infra.ts @@ -30,6 +30,9 @@ export default function ({ getService }: FtrProviderContext) { { type: 'cpu', }, + { + type: 'cpuTotal', + }, { type: 'diskSpaceUsage', }, @@ -95,6 +98,7 @@ export default function ({ getService }: FtrProviderContext) { ], metrics: [ { name: 'cpu', value: 0.44708333333333333 }, + { name: 'cpuTotal', value: 0 }, { name: 'diskSpaceUsage', value: 0 }, { name: 'memory', value: 0.4563333333333333 }, { name: 'memoryFree', value: 8573890560 }, @@ -155,7 +159,7 @@ export default function ({ getService }: FtrProviderContext) { ...basePayload, metrics: [ { - type: 'cpu', + type: 'cpuTotal', }, ], query: { bool: { filter: [{ term: { 'host.os.name': 'CentOS Linux' } }] } }, @@ -164,8 +168,8 @@ export default function ({ getService }: FtrProviderContext) { const names = (response.body as GetInfraMetricsResponsePayload).nodes.map((p) => p.name); expect(names).eql([ - 'gke-observability-8--observability-8--bc1afd95-ngmh', 'gke-observability-8--observability-8--bc1afd95-f0zc', + 'gke-observability-8--observability-8--bc1afd95-ngmh', 'gke-observability-8--observability-8--bc1afd95-nhhw', ]); }); @@ -175,7 +179,7 @@ export default function ({ getService }: FtrProviderContext) { ...basePayload, metrics: [ { - type: 'cpu', + type: 'cpuTotal', }, ], query: { bool: { filter: [{ term: { 'host.os.name': 'Ubuntu' } }] } }, @@ -192,7 +196,7 @@ export default function ({ getService }: FtrProviderContext) { ...basePayload, metrics: [ { - type: 'cpu', + type: 'cpuTotal', }, ], query: { @@ -207,9 +211,8 @@ export default function ({ getService }: FtrProviderContext) { const names = (response.body as GetInfraMetricsResponsePayload).nodes.map((p) => p.name); expect(names).eql([ - 'gke-observability-8--observability-8--bc1afd95-ngmh', 'gke-observability-8--observability-8--bc1afd95-f0zc', - , + 'gke-observability-8--observability-8--bc1afd95-ngmh', ]); }); @@ -246,7 +249,7 @@ export default function ({ getService }: FtrProviderContext) { const response = await makeRequest({ invalidBody, expectedHTTPCode: 400 }); expect(normalizeNewLine(response.body.message)).to.be( - '[request body]: Failed to validate: in metrics/0/type: "any" does not match expected type "cpu" | "normalizedLoad1m" | "diskSpaceUsage" | "memory" | "memoryFree" | "rx" | "tx" | "rxV2" | "txV2"' + '[request body]: Failed to validate: in metrics/0/type: "any" does not match expected type "cpu" | "cpuTotal" | "normalizedLoad1m" | "diskSpaceUsage" | "memory" | "memoryFree" | "rx" | "tx" | "rxV2" | "txV2"' ); }); diff --git a/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts index 7954a7b31e900..4e229c133b4fd 100644 --- a/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts +++ b/x-pack/test/api_integration/apis/ml/trained_models/model_downloads.ts @@ -51,6 +51,7 @@ export default ({ getService }: FtrProviderContext) => { { modelName: 'elser', hidden: true, + supported: false, version: 1, config: { input: { @@ -72,6 +73,7 @@ export default ({ getService }: FtrProviderContext) => { description: 'Elastic Learned Sparse EncodeR v2', type: ['elastic', 'pytorch', 'text_expansion'], model_id: '.elser_model_2', + supported: true, ...(isIntelBased ? { default: true } : { recommended: true }), }, { @@ -87,7 +89,7 @@ export default ({ getService }: FtrProviderContext) => { description: 'Elastic Learned Sparse EncodeR v2, optimized for linux-x86_64', type: ['elastic', 'pytorch', 'text_expansion'], model_id: '.elser_model_2_linux-x86_64', - ...(isIntelBased ? { recommended: true } : {}), + ...(isIntelBased ? { recommended: true, supported: true } : { supported: false }), }, { modelName: 'e5', @@ -102,6 +104,7 @@ export default ({ getService }: FtrProviderContext) => { licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], model_id: '.multilingual-e5-small', + supported: true, ...(isIntelBased ? { default: true } : { recommended: true }), }, { @@ -120,7 +123,7 @@ export default ({ getService }: FtrProviderContext) => { licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', type: ['pytorch', 'text_embedding'], model_id: '.multilingual-e5-small_linux-x86_64', - ...(isIntelBased ? { recommended: true } : {}), + ...(isIntelBased ? { recommended: true, supported: true } : { supported: false }), }, ]); }); diff --git a/x-pack/test/api_integration/apis/painless_lab/config.ts b/x-pack/test/api_integration/apis/painless_lab/config.ts deleted file mode 100644 index 5f335f116fefe..0000000000000 --- a/x-pack/test/api_integration/apis/painless_lab/config.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 { FtrConfigProviderContext } from '@kbn/test'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const baseIntegrationTestsConfig = await readConfigFile(require.resolve('../../config.ts')); - - return { - ...baseIntegrationTestsConfig.getAll(), - testFiles: [require.resolve('.')], - }; -} diff --git a/x-pack/test/api_integration/apis/security/index.ts b/x-pack/test/api_integration/apis/security/index.ts index 0e3f85d22a4ad..949862992a804 100644 --- a/x-pack/test/api_integration/apis/security/index.ts +++ b/x-pack/test/api_integration/apis/security/index.ts @@ -21,5 +21,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./roles')); loadTestFile(require.resolve('./users')); loadTestFile(require.resolve('./privileges')); + loadTestFile(require.resolve('./roles_bulk')); }); } diff --git a/x-pack/test/api_integration/apis/security/roles_bulk.ts b/x-pack/test/api_integration/apis/security/roles_bulk.ts new file mode 100644 index 0000000000000..52c6f9f21ab29 --- /dev/null +++ b/x-pack/test/api_integration/apis/security/roles_bulk.ts @@ -0,0 +1,425 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const es = getService('es'); + const supertest = getService('supertest'); + const config = getService('config'); + const basic = config.get('esTestCluster.license') === 'basic'; + + describe('Roles Bulk', () => { + after(async () => { + await supertest.delete('/api/security/role/bulk_role_1').set('kbn-xsrf', 'xxx').expect(204); + await supertest.delete('/api/security/role/bulk_role_2').set('kbn-xsrf', 'xxx').expect(204); + await supertest + .delete('/api/security/role/bulk_role_valid') + .set('kbn-xsrf', 'xxx') + .expect(204); + await supertest + .delete('/api/security/role/bulk_role_with_privilege_1') + .set('kbn-xsrf', 'xxx') + .expect(204); + await supertest + .delete('/api/security/role/bulk_role_with_privilege_2') + .set('kbn-xsrf', 'xxx') + .expect(204); + await supertest + .delete('/api/security/role/bulk_role_to_update_1') + .set('kbn-xsrf', 'xxx') + .expect(204); + await supertest + .delete('/api/security/role/bulk_role_to_update_2') + .set('kbn-xsrf', 'xxx') + .expect(204); + + const emptyRoles = await es.security.getRole( + { name: 'bulk_role_1,bulk_role_2' }, + { ignore: [404] } + ); + expect(emptyRoles).to.eql({}); + const rolesWithPrivileges = await es.security.getRole( + { name: 'bulk_role_with_privilege_1,bulk_role_with_privilege_2,bulk_role_valid' }, + { ignore: [404] } + ); + expect(rolesWithPrivileges).to.eql({}); + + const rolesToUpdate = await es.security.getRole( + { name: 'bulk_role_to_update_1,bulk_role_to_update_2' }, + { ignore: [404] } + ); + expect(rolesToUpdate).to.eql({}); + }); + + describe('Create Roles', () => { + it('should allow us to create empty roles', async () => { + await supertest + .post('/api/security/roles') + .set('kbn-xsrf', 'xxx') + .send({ + roles: { + bulk_role_1: {}, + bulk_role_2: {}, + }, + }) + .expect(200) + .then((response) => { + expect(response.body).to.eql({ created: ['bulk_role_1', 'bulk_role_2'] }); + }); + }); + + it('should create roles with kibana and elasticsearch privileges', async () => { + await supertest + .post('/api/security/roles') + .set('kbn-xsrf', 'xxx') + .send({ + roles: { + bulk_role_with_privilege_1: { + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + run_as: ['watcher_user'], + }, + kibana: [ + { + base: ['read'], + }, + { + feature: { + dashboard: ['read'], + discover: ['all'], + ml: ['all'], + }, + spaces: ['marketing', 'sales'], + }, + ], + }, + bulk_role_with_privilege_2: { + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + }, + ], + run_as: ['watcher_user'], + }, + kibana: [ + { + base: ['read'], + }, + { + feature: { + dashboard: ['read'], + discover: ['all'], + ml: ['all'], + }, + spaces: ['marketing', 'sales'], + }, + ], + }, + }, + }) + .expect(200); + + const role = await es.security.getRole({ name: 'bulk_role_with_privilege_1' }); + expect(role).to.eql({ + bulk_role_with_privilege_1: { + metadata: {}, + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_discover.all', 'feature_ml.all'], + resources: ['space:marketing', 'space:sales'], + }, + ], + run_as: ['watcher_user'], + transient_metadata: { + enabled: true, + }, + }, + }); + }); + + it(`should ${basic ? 'not' : ''} create a role with kibana and FLS/DLS elasticsearch + privileges on ${basic ? 'basic' : 'trial'} licenses`, async () => { + await supertest + .post('/api/security/roles') + .set('kbn-xsrf', 'xxx') + .send({ + roles: { + role_with_privileges_dls_fls: { + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + field_security: { + grant: ['*'], + except: ['geo.*'], + }, + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + query: `{ "match": { "geo.src": "CN" } }`, + }, + ], + run_as: ['watcher_user'], + }, + }, + }, + }) + .expect(200) + .then((response) => { + const { errors, created } = response.body; + if (basic) { + expect(created).to.be(undefined); + expect(errors).to.have.property('role_with_privileges_dls_fls'); + expect(errors.role_with_privileges_dls_fls.type).to.be('security_exception'); + expect(errors.role_with_privileges_dls_fls.reason).to.contain( + `current license is non-compliant for [field and document level security]` + ); + } else { + expect(created).to.eql(['role_with_privileges_dls_fls']); + expect(errors).to.be(undefined); + } + }); + }); + + it('should return noop if roles exist and did not change', async () => { + await supertest + .post('/api/security/roles') + .set('kbn-xsrf', 'xxx') + .send({ + roles: { + bulk_role_1: {}, + bulk_role_2: {}, + }, + }) + .expect(200) + .then((response) => { + expect(response.body).to.eql({ noop: ['bulk_role_1', 'bulk_role_2'] }); + }); + }); + + it('should return validation errors for roles that failed', async () => { + await supertest + .post('/api/security/roles') + .set('kbn-xsrf', 'xxx') + .send({ + roles: { + bulk_role_es_invalid: { + elasticsearch: { + cluster: ['bla'], + }, + }, + bulk_role_kibana_invalid: { + kibana: [ + { + spaces: ['bar-space'], + base: [], + feature: { + fleetv2: ['all', 'read'], + }, + }, + ], + }, + bulk_role_valid: { + elasticsearch: { + cluster: ['all'], + }, + }, + }, + }) + .expect(200) + .then((response) => { + const { created, errors } = response.body; + expect(created).to.eql(['bulk_role_valid']); + expect(errors).to.have.property('bulk_role_es_invalid'); + expect(errors).to.have.property('bulk_role_kibana_invalid'); + }); + }); + }); + + describe('Update Roles', () => { + it('should update roles with elasticsearch, kibana and other applications privileges', async () => { + await es.security.putRole({ + name: 'bulk_role_to_update_1', + body: { + cluster: ['monitor'], + indices: [ + { + names: ['beats-*'], + privileges: ['write'], + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['read'], + resources: ['*'], + }, + { + application: 'logstash-default', + privileges: ['logstash-privilege'], + resources: ['*'], + }, + ], + run_as: ['reporting_user'], + metadata: { + bar: 'old-metadata', + }, + }, + }); + await es.security.putRole({ name: 'bulk_role_to_update_2', body: {} }); + + await supertest + .post('/api/security/roles') + .set('kbn-xsrf', 'xxx') + .send({ + roles: { + bulk_role_to_update_1: { + metadata: { + foo: 'test-metadata', + }, + elasticsearch: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + allow_restricted_indices: true, + }, + ], + run_as: ['watcher_user'], + }, + kibana: [ + { + feature: { + dashboard: ['read'], + dev_tools: ['all'], + }, + spaces: ['*'], + }, + { + base: ['all'], + spaces: ['marketing', 'sales'], + }, + ], + }, + bulk_role_to_update_2: { + kibana: [ + { + feature: { + dashboard: ['read'], + dev_tools: ['all'], + }, + spaces: ['*'], + }, + { + base: ['all'], + spaces: ['observability', 'sales'], + }, + ], + }, + }, + }) + .expect(200) + .then((response) => { + expect(response.body).to.eql({ + updated: ['bulk_role_to_update_1', 'bulk_role_to_update_2'], + }); + }); + + const role = await es.security.getRole({ + name: 'bulk_role_to_update_1,bulk_role_to_update_2', + }); + + expect(role).to.eql({ + bulk_role_to_update_1: { + cluster: ['manage'], + indices: [ + { + names: ['logstash-*'], + privileges: ['read', 'view_index_metadata'], + allow_restricted_indices: true, + }, + ], + metadata: { + bar: 'old-metadata', + foo: 'test-metadata', + }, + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_dev_tools.all'], + resources: ['*'], + }, + { + application: 'kibana-.kibana', + privileges: ['space_all'], + resources: ['space:marketing', 'space:sales'], + }, + { + application: 'logstash-default', + privileges: ['logstash-privilege'], + resources: ['*'], + }, + ], + run_as: ['watcher_user'], + transient_metadata: { + enabled: true, + }, + }, + bulk_role_to_update_2: { + cluster: [], + indices: [], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_dashboard.read', 'feature_dev_tools.all'], + resources: ['*'], + }, + { + application: 'kibana-.kibana', + privileges: ['space_all'], + resources: ['space:observability', 'space:sales'], + }, + ], + run_as: [], + metadata: {}, + transient_metadata: { + enabled: true, + }, + }, + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/security/security_basic.ts b/x-pack/test/api_integration/apis/security/security_basic.ts index 709f738b4e9bb..c0815f0aefe69 100644 --- a/x-pack/test/api_integration/apis/security/security_basic.ts +++ b/x-pack/test/api_integration/apis/security/security_basic.ts @@ -21,5 +21,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./roles')); loadTestFile(require.resolve('./users')); loadTestFile(require.resolve('./privileges_basic')); + loadTestFile(require.resolve('./roles_bulk')); }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/README.md b/x-pack/test/api_integration/deployment_agnostic/README.md new file mode 100644 index 0000000000000..eff834b7b1db9 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/README.md @@ -0,0 +1,183 @@ +# Deployment-Agnostic Tests Guidelines + +## Definition +A deployment-agnostic API integration test is a test suite that fulfills the following criteria: + +**Functionality**: It tests Kibana APIs that are logically identical in both stateful and serverless environments for the same roles. + +**Design**: The test design is clean and does not require additional logic to execute in either stateful or serverless environments. + +## Tests Design Requirements +A deployment-agnostic test is contained within a single test file and always utilizes the [DeploymentAgnosticFtrProviderContext](https://github.com/elastic/kibana/blob/main/x-pack/test/api_integration/deployment_agnostic/ftr_provider_context.d.ts) to load compatible FTR services. A compatible FTR service must support: + +- **Serverless**: Both local environments and MKI (Managed Kubernetes Infrastructure). +- **Stateful**: Both local environments and Cloud deployments. + +To achieve this, services cannot use `supertest`, which employs an operator user for serverless and a system index superuser for stateful setups. Instead, services should use a combination of `supertestWithoutAuth` and `samlAuth` to generate an API key for user roles and make API calls. For example, see the [data_view_api.ts](https://github.com/elastic/kibana/blob/main/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts) service. + +### How It Works +Most existing stateful tests use basic authentication for API testing. In contrast, serverless tests use SAML authentication with project-specific role mapping. + +Since both Elastic Cloud (ESS) and Serverless rely on SAML authentication by default, and stateful deployments also support SAML, *deployment-agnostic tests configure Elasticsearch and Kibana with SAML authentication to use the same authentication approach in all cases*. For roles, stateful deployments define 'viewer', 'editor', and 'admin' roles with serverless-alike permissions. + +### When to Create Separate Tests +While the deployment-agnostic testing approach is beneficial, it should not compromise the quality and simplicity of the tests. Here are some scenarios where separate test files are recommended: + +- **Role-Specific Logic**: If API access or logic depends on roles that differ across deployments. +- **Environment Constraints**: If a test can only run locally and not on MKI or Cloud deployments. +- **Complex Logic**: If the test logic requires splitting across multiple locations. + +## File Structure +We recommend following this structure to simplify maintenance and allow other teams to reuse code (e.g., FTR services) created by different teams: + +``` +x-pack/test/<my_own_api_integration_folder> +├─ deployment_agnostic +│ ├─ apis +│ │ ├─ <api_1> +│ │ │ ├─ <test_1_1> +│ │ │ ├─ <test_1_2> +│ │ ├─ <api_2> +│ │ │ ├─ <test_2_1> +│ │ │ ├─ <test_2_2> +│ ├─ services +│ │ ├─ index.ts // only services from 'x-pack/test/api_integration/deployment_agnostic/services' +│ │ ├─ <deployment_agnostic_service_1>.ts +│ │ ├─ <deployment_agnostic_service_2>.ts +│ ├─ ftr_provider_context.d.ts // with types of services from './services' +├─ stateful.index.ts +├─ stateful.config.ts +├─ <serverless_project>.index.ts // e.g., oblt.index.ts +├─ <serverless_project>.serverless.config.ts // e.g., oblt.serverless.config.ts +``` + +## Step-by-Step Guide +1. Define Deployment-Agnostic Services + +Under `x-pack/test/<my_own_api_integration_folder>/deployment_agnostic/services`, create `index.ts` and load default services like `samlAuth` and `superuserWithoutAuth`: + +```ts +import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; +import { services as deploymentAgnosticServices } from './../../api_integration/deployment_agnostic/services'; + +export type { + InternalRequestHeader, + RoleCredentials, + SupertestWithoutAuthProviderType, +} from '@kbn/ftr-common-functional-services'; + +export const services = { + ...deploymentAgnosticServices, + // create a new deployment-agnostic service and load here +}; +``` + +We suggest adding new services to `x-pack/test/api_integration/deployment_agnostic/services` so other teams can benefit from them. + +2. Create `DeploymentAgnosticFtrProviderContext` with Services Defined in Step 2 + +Create `ftr_provider_context.d.ts` and export `DeploymentAgnosticFtrProviderContext`: +```ts +import { GenericFtrProviderContext } from '@kbn/test'; +import { services } from './services'; + +export type DeploymentAgnosticFtrProviderContext = GenericFtrProviderContext<typeof services, {}>; +``` + +3. Add Tests + +Add test files to `x-pack/test/<my_own_api_integration_folder>/deployment_agnostic/apis/<my_api>`: + +test example +```ts +export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalHeaders: InternalRequestHeader; + + describe('compression', () => { + before(async () => { + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + internalHeaders = samlAuth.getInternalRequestHeader(); + }); + after(async () => { + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); + }); + describe('against an application page', () => { + it(`uses compression when there isn't a referer`, async () => { + const response = await supertestWithoutAuth + .get('/app/kibana') + .set('accept-encoding', 'gzip') + .set(internalHeaders) + .set(roleAuthc.apiKeyHeader); + expect(response.header).to.have.property('content-encoding', 'gzip'); + }); + }); + }); +} +``` +Load all test files in `index.ts` under the same folder. + +4. Add Tests Entry File and FTR Config File for **Stateful** Deployment + +Create `stateful.index.ts` tests entry file and load tests: + +```ts +import { DeploymentAgnosticFtrProviderContext } from './ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('apis', () => { + loadTestFile(require.resolve('./apis/<my_api>')); + }); +} +``` + +Create `stateful.config.ts` and link tests entry file: + +```ts +import { createStatefulTestConfig } from './../../api_integration/deployment_agnostic/default_configs/stateful.config.base'; + +export default createStatefulTestConfig({ + testFiles: [require.resolve('./stateful.index.ts')], + junit: { + reportName: 'Stateful - Deployment-agnostic API Integration Tests', + }, + // extra arguments + esServerArgs: [], + kbnServerArgs: [], +}); +``` +5. Add Tests Entry File and FTR Config File for Specific **Serverless** Project + +Example for Observability project: + +oblt.index.ts +```ts +import { DeploymentAgnosticFtrProviderContext } from './ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('Serverless Observability - Deployment-agnostic api integration tests', () => { + loadTestFile(require.resolve('./apis/<my_api>')); + }); +} +``` + +oblt.serverless.config.ts +```ts +import { createServerlessTestConfig } from './../../api_integration/deployment_agnostic/default_configs/serverless.config.base'; + +export default createServerlessTestConfig({ + serverlessProject: 'oblt', + testFiles: [require.resolve('./oblt.index.ts')], + junit: { + reportName: 'Serverless Observability - Deployment-agnostic API Integration Tests', + }, +}); +``` + +ES and Kibana project-specific arguments are defined and loaded from `serverless.config.base`. These arguments are copied from the Elasticsearch and Kibana controller repositories. + +Note: The FTR (Functional Test Runner) does not have the capability to provision custom ES/Kibana server arguments into the serverless project. Any custom arguments listed explicitly in this config file will apply **only to a local environment**. + +6. Add FTR Configs Path to FTR Manifest Files Located in `.buildkite/` diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/console/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/console/index.ts new file mode 100644 index 0000000000000..4558f6818542f --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/console/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('console', () => { + loadTestFile(require.resolve('./spec_definitions')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/console/spec_definitions.ts b/x-pack/test/api_integration/deployment_agnostic/apis/console/spec_definitions.ts new file mode 100644 index 0000000000000..a2c8115e4ea0d --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/console/spec_definitions.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 expect from '@kbn/expect'; +import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services'; +import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalHeaders: InternalRequestHeader; + + describe('GET /api/console/api_server', () => { + before(async () => { + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + internalHeaders = samlAuth.getInternalRequestHeader(); + }); + after(async () => { + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); + }); + it('returns autocomplete definitions', async () => { + const { body } = await supertestWithoutAuth + .get('/api/console/api_server') + .set(roleAuthc.apiKeyHeader) + .set(internalHeaders) + .set('kbn-xsrf', 'true') + .expect(200); + expect(body.es).to.be.ok(); + const { + es: { name, globals, endpoints }, + } = body; + expect(name).to.be.ok(); + expect(Object.keys(globals).length).to.be.above(0); + expect(Object.keys(endpoints).length).to.be.above(0); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/core/compression.ts b/x-pack/test/api_integration/deployment_agnostic/apis/core/compression.ts new file mode 100644 index 0000000000000..d1aa1cfb45153 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/core/compression.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services'; +import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalHeaders: InternalRequestHeader; + + describe('compression', () => { + before(async () => { + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + internalHeaders = samlAuth.getInternalRequestHeader(); + }); + after(async () => { + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); + }); + describe('against an application page', () => { + it(`uses compression when there isn't a referer`, async () => { + const response = await supertestWithoutAuth + .get('/app/kibana') + .set('accept-encoding', 'gzip') + .set(internalHeaders) + .set(roleAuthc.apiKeyHeader); + expect(response.header).to.have.property('content-encoding', 'gzip'); + }); + + it(`uses compression when there is a whitelisted referer`, async () => { + const response = await supertestWithoutAuth + .get('/app/kibana') + .set('accept-encoding', 'gzip') + .set(internalHeaders) + .set('referer', 'https://some-host.com') + .set(roleAuthc.apiKeyHeader); + expect(response.header).to.have.property('content-encoding', 'gzip'); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/core/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/core/index.ts new file mode 100644 index 0000000000000..93e40d3d5b914 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/core/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('core', () => { + loadTestFile(require.resolve('./compression')); + }); +} diff --git a/x-pack/test/api_integration/apis/painless_lab/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/painless_lab/index.ts similarity index 67% rename from x-pack/test/api_integration/apis/painless_lab/index.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/painless_lab/index.ts index 63744b77312d7..ff59037fc1f06 100644 --- a/x-pack/test/api_integration/apis/painless_lab/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/painless_lab/index.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; -export default function ({ loadTestFile }: FtrProviderContext) { +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { describe('Painless Lab', () => { loadTestFile(require.resolve('./painless_lab')); }); diff --git a/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts b/x-pack/test/api_integration/deployment_agnostic/apis/painless_lab/painless_lab.ts similarity index 60% rename from x-pack/test/api_integration/apis/painless_lab/painless_lab.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/painless_lab/painless_lab.ts index b594220634d1d..7e7047ac9cb57 100644 --- a/x-pack/test/api_integration/apis/painless_lab/painless_lab.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/painless_lab/painless_lab.ts @@ -6,22 +6,34 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services'; +import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context'; const API_BASE_PATH = '/api/painless_lab'; -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); +export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { + const samlAuth = getService('samlAuth'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalHeaders: InternalRequestHeader; - describe('Painless Lab', function () { + describe('Painless Lab Routes', function () { + before(async () => { + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + internalHeaders = samlAuth.getInternalRequestHeader(); + }); + after(async () => { + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); + }); describe('Execute', () => { it('should execute a valid painless script', async () => { const script = '"{\\n \\"script\\": {\\n \\"source\\": \\"return true;\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${API_BASE_PATH}/execute`) - .set('kbn-xsrf', 'xxx') + .set(internalHeaders) + .set(roleAuthc.apiKeyHeader) .set('Content-Type', 'application/json;charset=UTF-8') .send(script) .expect(200); @@ -35,10 +47,11 @@ export default function ({ getService }: FtrProviderContext) { const invalidScript = '"{\\n \\"script\\": {\\n \\"source\\": \\"foobar\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${API_BASE_PATH}/execute`) - .set('kbn-xsrf', 'xxx') + .set(internalHeaders) .set('Content-Type', 'application/json;charset=UTF-8') + .set(roleAuthc.apiKeyHeader) .send(invalidScript) .expect(200); diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts new file mode 100644 index 0000000000000..606a71835b317 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/serverless.config.base.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrConfigProviderContext, Config } from '@kbn/test'; + +import { ServerlessProjectType } from '@kbn/es'; +import { services } from '../services'; + +interface CreateTestConfigOptions { + serverlessProject: ServerlessProjectType; + esServerArgs?: string[]; + kbnServerArgs?: string[]; + testFiles: string[]; + junit: { reportName: string }; + suiteTags?: { include?: string[]; exclude?: string[] }; +} + +// include settings from elasticsearch controller +// https://github.com/elastic/elasticsearch-controller/blob/main/helm/values.yaml +const esServerArgsFromController = { + es: [], + oblt: [ + 'xpack.apm_data.enabled=true', + // for ML, data frame analytics are not part of this project type + 'xpack.ml.dfa.enabled=false', + ], + security: [ + 'xpack.security.authc.api_key.cache.max_keys=70000', + 'data_streams.lifecycle.retention.factory_default=365d', + 'data_streams.lifecycle.retention.factory_max=365d', + ], +}; + +// include settings from kibana controller +// https://github.com/elastic/kibana-controller/blob/main/internal/controllers/kibana/config/config_settings.go +const kbnServerArgsFromController = { + es: [ + // useful for testing (also enabled in MKI QA) + '--coreApp.allowDynamicConfigOverrides=true', + ], + oblt: [ + '--coreApp.allowDynamicConfigOverrides=true', + // defined in MKI control plane + '--xpack.uptime.service.manifestUrl=mockDevUrl', + ], + security: [ + '--coreApp.allowDynamicConfigOverrides=true', + // disable fleet task that writes to metrics.fleet_server.* data streams, impacting functional tests + `--xpack.task_manager.unsafe.exclude_task_types=${JSON.stringify(['Fleet-Metrics-Task'])}`, + ], +}; + +export function createServerlessTestConfig(options: CreateTestConfigOptions) { + return async ({ readConfigFile }: FtrConfigProviderContext): Promise<Config> => { + const svlSharedConfig = await readConfigFile( + require.resolve('@kbn/test-suites-serverless/shared/config.base') + ); + + return { + ...svlSharedConfig.getAll(), + + services: { + ...services, + }, + esTestCluster: { + ...svlSharedConfig.get('esTestCluster'), + serverArgs: [ + ...svlSharedConfig.get('esTestCluster.serverArgs'), + ...esServerArgsFromController[options.serverlessProject], + ...(options.esServerArgs ?? []), + ], + }, + kbnTestServer: { + ...svlSharedConfig.get('kbnTestServer'), + serverArgs: [ + ...svlSharedConfig.get('kbnTestServer.serverArgs'), + ...kbnServerArgsFromController[options.serverlessProject], + `--serverless=${options.serverlessProject}`, + ...(options.kbnServerArgs || []), + ], + }, + testFiles: options.testFiles, + junit: options.junit, + suiteTags: options.suiteTags, + }; + }; +} diff --git a/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts new file mode 100644 index 0000000000000..c784ff071895b --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/default_configs/stateful.config.base.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { + MOCK_IDP_REALM_NAME, + MOCK_IDP_ENTITY_ID, + MOCK_IDP_ATTRIBUTE_PRINCIPAL, + MOCK_IDP_ATTRIBUTE_ROLES, + MOCK_IDP_ATTRIBUTE_EMAIL, + MOCK_IDP_ATTRIBUTE_NAME, +} from '@kbn/mock-idp-utils'; +import { + esTestConfig, + kbnTestConfig, + systemIndicesSuperuser, + FtrConfigProviderContext, +} from '@kbn/test'; +import { services } from '../services'; + +interface CreateTestConfigOptions { + esServerArgs?: string[]; + kbnServerArgs?: string[]; + testFiles: string[]; + junit: { reportName: string }; + suiteTags?: { include?: string[]; exclude?: string[] }; +} + +export function createStatefulTestConfig(options: CreateTestConfigOptions) { + return async ({ readConfigFile }: FtrConfigProviderContext) => { + const xPackAPITestsConfig = await readConfigFile(require.resolve('../../config.ts')); + + // TODO: move to kbn-es because currently metadata file has hardcoded entityID and Location + const idpPath = require.resolve( + '@kbn/security-api-integration-helpers/saml/idp_metadata_mock_idp.xml' + ); + + const servers = { + kibana: { + ...kbnTestConfig.getUrlParts(systemIndicesSuperuser), + protocol: process.env.TEST_CLOUD ? 'https' : 'http', + }, + elasticsearch: { + ...esTestConfig.getUrlParts(), + protocol: process.env.TEST_CLOUD ? 'https' : 'http', + }, + }; + + const kbnUrl = `${servers.kibana.protocol}://${servers.kibana.hostname}:${servers.kibana.port}`; + + return { + servers, + testFiles: options.testFiles, + security: { disableTestUser: true }, + services, + junit: options.junit, + suiteTags: options.suiteTags, + + esTestCluster: { + ...xPackAPITestsConfig.get('esTestCluster'), + serverArgs: [ + ...xPackAPITestsConfig.get('esTestCluster.serverArgs'), + ...(options.esServerArgs ?? []), + 'xpack.security.authc.token.enabled=true', + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.order=0`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.idp.metadata.path=${idpPath}`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.idp.entity_id=${MOCK_IDP_ENTITY_ID}`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.sp.entity_id=${kbnUrl}`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.sp.acs=${kbnUrl}/api/security/saml/callback`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.sp.logout=${kbnUrl}/logout`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.principal=${MOCK_IDP_ATTRIBUTE_PRINCIPAL}`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.groups=${MOCK_IDP_ATTRIBUTE_ROLES}`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.name=${MOCK_IDP_ATTRIBUTE_NAME}`, + `xpack.security.authc.realms.saml.${MOCK_IDP_REALM_NAME}.attributes.mail=${MOCK_IDP_ATTRIBUTE_EMAIL}`, + ], + }, + + kbnTestServer: { + ...xPackAPITestsConfig.get('kbnTestServer'), + serverArgs: [ + ...xPackAPITestsConfig.get('kbnTestServer.serverArgs'), + ...(options.kbnServerArgs || []), + '--xpack.security.authc.selector.enabled=false', + `--xpack.security.authc.providers=${JSON.stringify({ + saml: { 'cloud-saml-kibana': { order: 0, realm: MOCK_IDP_REALM_NAME } }, + basic: { 'cloud-basic': { order: 1 } }, + })}`, + `--server.publicBaseUrl=${servers.kibana.protocol}://${servers.kibana.hostname}:${servers.kibana.port}`, + ], + }, + }; + }; +} diff --git a/x-pack/test/api_integration/deployment_agnostic/ftr_provider_context.d.ts b/x-pack/test/api_integration/deployment_agnostic/ftr_provider_context.d.ts new file mode 100644 index 0000000000000..81df490d79428 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/ftr_provider_context.d.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { GenericFtrProviderContext } from '@kbn/test'; + +import { services } from './services'; + +export type DeploymentAgnosticFtrProviderContext = GenericFtrProviderContext<typeof services, {}>; diff --git a/x-pack/test/api_integration/deployment_agnostic/oblt.index.ts b/x-pack/test/api_integration/deployment_agnostic/oblt.index.ts new file mode 100644 index 0000000000000..d81415e0554dd --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/oblt.index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { DeploymentAgnosticFtrProviderContext } from './ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('Serverless Observability - Deployment-agnostic api integration tests', () => { + loadTestFile(require.resolve('./apis/console')); + loadTestFile(require.resolve('./apis/core')); + loadTestFile(require.resolve('./apis/painless_lab')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/oblt.serverless.config.ts b/x-pack/test/api_integration/deployment_agnostic/oblt.serverless.config.ts new file mode 100644 index 0000000000000..52e1ba2d431ac --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/oblt.serverless.config.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 { createServerlessTestConfig } from './default_configs/serverless.config.base'; + +export default createServerlessTestConfig({ + serverlessProject: 'oblt', + testFiles: [require.resolve('./oblt.index.ts')], + junit: { + reportName: 'Serverless Observability - Deployment-agnostic API Integration Tests', + }, +}); diff --git a/x-pack/test/api_integration/deployment_agnostic/search.index.ts b/x-pack/test/api_integration/deployment_agnostic/search.index.ts new file mode 100644 index 0000000000000..740520f032f0f --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/search.index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { DeploymentAgnosticFtrProviderContext } from './ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('Serverless Search - Deployment-agnostic api integration tests', () => { + loadTestFile(require.resolve('./apis/console')); + loadTestFile(require.resolve('./apis/core')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/search.serverless.config.ts b/x-pack/test/api_integration/deployment_agnostic/search.serverless.config.ts new file mode 100644 index 0000000000000..5e90b71d69550 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/search.serverless.config.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 { createServerlessTestConfig } from './default_configs/serverless.config.base'; + +export default createServerlessTestConfig({ + serverlessProject: 'es', + testFiles: [require.resolve('./search.index.ts')], + junit: { + reportName: 'Serverless Search - Deployment-agnostic API Integration Tests', + }, +}); diff --git a/x-pack/test/api_integration/deployment_agnostic/security.index.ts b/x-pack/test/api_integration/deployment_agnostic/security.index.ts new file mode 100644 index 0000000000000..9a9db972bcd5d --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/security.index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { DeploymentAgnosticFtrProviderContext } from './ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('Security Search - Deployment-agnostic api integration tests', () => { + loadTestFile(require.resolve('./apis/console')); + loadTestFile(require.resolve('./apis/core')); + loadTestFile(require.resolve('./apis/painless_lab')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/security.serverless.config.ts b/x-pack/test/api_integration/deployment_agnostic/security.serverless.config.ts new file mode 100644 index 0000000000000..d3a30cc95d820 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/security.serverless.config.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 { createServerlessTestConfig } from './default_configs/serverless.config.base'; + +export default createServerlessTestConfig({ + serverlessProject: 'security', + testFiles: [require.resolve('./security.index.ts')], + junit: { + reportName: 'Serverless Security - Deployment-agnostic API Integration Tests', + }, +}); diff --git a/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts b/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts new file mode 100644 index 0000000000000..c22db40882b60 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RoleCredentials } from '@kbn/ftr-common-functional-services'; +import { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; + +export function DataViewApiProvider({ getService }: DeploymentAgnosticFtrProviderContext) { + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const samlAuth = getService('samlAuth'); + + return { + async create({ + roleAuthc, + id, + name, + title, + }: { + roleAuthc: RoleCredentials; + id: string; + name: string; + title: string; + }) { + const { body } = await supertestWithoutAuth + .post(`/api/content_management/rpc/create`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .set(samlAuth.getCommonRequestHeader()) + .send({ + contentTypeId: 'index-pattern', + data: { + fieldAttrs: '{}', + title, + timeFieldName: '@timestamp', + sourceFilters: '[]', + fields: '[]', + fieldFormatMap: '{}', + typeMeta: '{}', + runtimeFieldMap: '{}', + name, + }, + options: { id }, + version: 1, + }); + return body; + }, + + async delete({ roleAuthc, id }: { roleAuthc: RoleCredentials; id: string }) { + const { body } = await supertestWithoutAuth + .post(`/api/content_management/rpc/create`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .set(samlAuth.getCommonRequestHeader()) + .send({ + contentTypeId: 'index-pattern', + id, + options: { force: true }, + version: 1, + }); + return body; + }, + }; +} diff --git a/x-pack/test/api_integration/deployment_agnostic/services/deployment_agnostic_services.ts b/x-pack/test/api_integration/deployment_agnostic/services/deployment_agnostic_services.ts new file mode 100644 index 0000000000000..38222c096bed0 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/services/deployment_agnostic_services.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import _ from 'lodash'; +import { services as apiIntegrationServices } from '../../services'; + +/** + * Load only services that support both stateful & serverless deployments (including Cloud/MKI), + * e.g. `randomness` or `retry` are deployment agnostic + */ +export const deploymentAgnosticServices = _.pick(apiIntegrationServices, [ + 'supertest', // TODO: review its behaviour + 'es', + 'esArchiver', + 'esSupertest', // TODO: review its behaviour + 'indexPatterns', + 'ingestPipelines', + 'kibanaServer', + // 'ml', depends on 'esDeleteAllIndices', can we make it deployment agnostic? + 'randomness', + 'retry', + 'security', + 'usageAPI', +]); diff --git a/x-pack/test/api_integration/deployment_agnostic/services/index.ts b/x-pack/test/api_integration/deployment_agnostic/services/index.ts new file mode 100644 index 0000000000000..c1e70466f1b3c --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/services/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; +import { deploymentAgnosticServices } from './deployment_agnostic_services'; +import { DataViewApiProvider } from './data_view_api'; +import { SloApiProvider } from './slo_api'; + +export type { + InternalRequestHeader, + RoleCredentials, + SupertestWithoutAuthProviderType, +} from '@kbn/ftr-common-functional-services'; + +export const services = { + ...deploymentAgnosticServices, + supertestWithoutAuth: commonFunctionalServices.supertestWithoutAuth, + samlAuth: commonFunctionalServices.samlAuth, + dataViewApi: DataViewApiProvider, + sloApi: SloApiProvider, + // create a new deployment-agnostic service and load here +}; diff --git a/x-pack/test/api_integration/deployment_agnostic/services/slo_api.ts b/x-pack/test/api_integration/deployment_agnostic/services/slo_api.ts new file mode 100644 index 0000000000000..4c83a536ffb36 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/services/slo_api.ts @@ -0,0 +1,203 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + fetchHistoricalSummaryParamsSchema, + FetchHistoricalSummaryResponse, +} from '@kbn/slo-schema'; +import * as t from 'io-ts'; +import { RoleCredentials } from '@kbn/ftr-common-functional-services'; +import { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; + +type DurationUnit = 'm' | 'h' | 'd' | 'w' | 'M'; + +interface Duration { + value: number; + unit: DurationUnit; +} + +interface WindowSchema { + id: string; + burnRateThreshold: number; + maxBurnRateThreshold: number; + longWindow: Duration; + shortWindow: Duration; + actionGroup: string; +} + +interface Dependency { + ruleId: string; + actionGroupsToSuppressOn: string[]; +} + +export interface SloBurnRateRuleParams { + sloId: string; + windows: WindowSchema[]; + dependencies?: Dependency[]; +} + +interface SloParams { + id?: string; + name: string; + description: string; + indicator: { + type: 'sli.kql.custom'; + params: { + index: string; + good: string; + total: string; + timestampField: string; + }; + }; + timeWindow: { + duration: string; + type: string; + }; + budgetingMethod: string; + objective: { + target: number; + }; + groupBy: string; +} + +type FetchHistoricalSummaryParams = t.OutputOf< + typeof fetchHistoricalSummaryParamsSchema.props.body +>; + +interface SloRequestParams { + id: string; + roleAuthc: RoleCredentials; +} + +export function SloApiProvider({ getService }: DeploymentAgnosticFtrProviderContext) { + const es = getService('es'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const samlAuth = getService('samlAuth'); + const retry = getService('retry'); + const config = getService('config'); + const retryTimeout = config.get('timeouts.try'); + const requestTimeout = 30 * 1000; + + return { + async create(slo: SloParams, roleAuthc: RoleCredentials) { + const { body } = await supertestWithoutAuth + .post(`/api/observability/slos`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .send(slo); + return body; + }, + + async delete({ id, roleAuthc }: SloRequestParams) { + const response = await supertestWithoutAuth + .delete(`/api/observability/slos/${id}`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()); + return response; + }, + + async fetchHistoricalSummary( + params: FetchHistoricalSummaryParams, + roleAuthc: RoleCredentials + ): Promise<FetchHistoricalSummaryResponse> { + const { body } = await supertestWithoutAuth + .post(`/internal/observability/slos/_historical_summary`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .send(params); + return body; + }, + + async waitForSloToBeDeleted({ id, roleAuthc }: SloRequestParams) { + return await retry.tryForTime(retryTimeout, async () => { + const response = await supertestWithoutAuth + .delete(`/api/observability/slos/${id}`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .timeout(requestTimeout); + if (!response.ok) { + throw new Error(`SLO with id '${id}' was not deleted`); + } + return response; + }); + }, + + async waitForSloCreated({ id, roleAuthc }: SloRequestParams) { + return await retry.tryForTime(retryTimeout, async () => { + const response = await supertestWithoutAuth + .get(`/api/observability/slos/${id}`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .timeout(requestTimeout); + if (response.body.id === undefined) { + throw new Error(`No SLO with id '${id}' found`); + } + return response.body; + }); + }, + + async waitForSloSummaryTempIndexToExist(index: string) { + return await retry.tryForTime(retryTimeout, async () => { + const indexExists = await es.indices.exists({ index, allow_no_indices: false }); + if (!indexExists) { + throw new Error(`SLO summary index '${index}' should exist`); + } + return indexExists; + }); + }, + + async getSloData({ sloId, indexName }: { sloId: string; indexName: string }) { + const response = await es.search({ + index: indexName, + body: { + query: { + bool: { + filter: [{ term: { 'slo.id': sloId } }], + }, + }, + }, + }); + return response; + }, + async waitForSloData({ id, indexName }: { id: string; indexName: string }) { + return await retry.tryForTime(retryTimeout, async () => { + const response = await es.search({ + index: indexName, + body: { + query: { + bool: { + filter: [{ term: { 'slo.id': id } }], + }, + }, + }, + }); + if (response.hits.hits.length === 0) { + throw new Error(`No hits found at index '${indexName}' for slo id='${id}'`); + } + return response; + }); + }, + async deleteAllSLOs(roleAuthc: RoleCredentials) { + const response = await supertestWithoutAuth + .get(`/api/observability/slos/_definitions`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .send() + .expect(200); + await Promise.all( + response.body.results.map(({ id }: { id: string }) => { + return supertestWithoutAuth + .delete(`/api/observability/slos/${id}`) + .set(roleAuthc.apiKeyHeader) + .set(samlAuth.getInternalRequestHeader()) + .send() + .expect(204); + }) + ); + }, + }; +} diff --git a/x-pack/test/api_integration/deployment_agnostic/stateful.config.ts b/x-pack/test/api_integration/deployment_agnostic/stateful.config.ts new file mode 100644 index 0000000000000..b4c22e735925c --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/stateful.config.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createStatefulTestConfig } from './default_configs/stateful.config.base'; + +export default createStatefulTestConfig({ + testFiles: [require.resolve('./stateful.index.ts')], + junit: { + reportName: 'Stateful - Deployment-agnostic API Integration Tests', + }, + // extra arguments + esServerArgs: [], + kbnServerArgs: [], +}); diff --git a/x-pack/test/api_integration/deployment_agnostic/stateful.index.ts b/x-pack/test/api_integration/deployment_agnostic/stateful.index.ts new file mode 100644 index 0000000000000..3e46c4d5b0472 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/stateful.index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from './ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('apis', () => { + loadTestFile(require.resolve('./apis/console')); + loadTestFile(require.resolve('./apis/core')); + loadTestFile(require.resolve('./apis/painless_lab')); + }); +} diff --git a/x-pack/test/api_integration/services/security_solution_api.gen.ts b/x-pack/test/api_integration/services/security_solution_api.gen.ts index 676fea3fd5364..ef706d2aca2cd 100644 --- a/x-pack/test/api_integration/services/security_solution_api.gen.ts +++ b/x-pack/test/api_integration/services/security_solution_api.gen.ts @@ -41,8 +41,22 @@ import { DeleteNoteRequestBodyInput } from '@kbn/security-solution-plugin/common import { DeleteRuleRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.gen'; import { DeleteTimelinesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/timeline/delete_timelines/delete_timelines_route.gen'; import { DeprecatedTriggerRiskScoreCalculationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; -import { EndpointIsolateRedirectRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/isolate_route.gen'; -import { EndpointUnisolateRedirectRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/unisolate_route.gen'; +import { EndpointExecuteActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/execute/execute.gen'; +import { EndpointFileDownloadRequestParamsInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/file_download/file_download.gen'; +import { EndpointFileInfoRequestParamsInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/file_info/file_info.gen'; +import { EndpointGetActionsDetailsRequestParamsInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/details/details.gen'; +import { EndpointGetActionsListRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/list/list.gen'; +import { EndpointGetActionsStatusRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/status/status.gen'; +import { EndpointGetFileActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/get_file/get_file.gen'; +import { EndpointGetProcessesActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen'; +import { EndpointIsolateActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/isolate/isolate.gen'; +import { EndpointIsolateRedirectRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/isolate/deprecated_isolate.gen'; +import { EndpointKillProcessActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/kill_process/kill_process.gen'; +import { EndpointScanActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/scan/scan.gen'; +import { EndpointSuspendProcessActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/suspend_process/suspend_process.gen'; +import { EndpointUnisolateActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen'; +import { EndpointUnisolateRedirectRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/unisolate/deprecated_unisolate.gen'; +import { EndpointUploadActionRequestBodyInput } from '@kbn/security-solution-plugin/common/api/endpoint/actions/response_actions/upload/upload.gen'; import { ExportRulesRequestQueryInput, ExportRulesRequestBodyInput, @@ -54,15 +68,16 @@ import { import { FinalizeAlertsMigrationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen'; import { FindAssetCriticalityRecordsRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/list_asset_criticality.gen'; import { FindRulesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/find_rules/find_rules_route.gen'; -import { GetAgentPolicySummaryRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/policy.gen'; +import { GetAgentPolicySummaryRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/deprecated_agent_policy_summary.gen'; import { GetAssetCriticalityRecordRequestQueryInput } from '@kbn/security-solution-plugin/common/api/entity_analytics/asset_criticality/get_asset_criticality.gen'; import { GetDraftTimelinesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/timeline/get_draft_timelines/get_draft_timelines_route.gen'; +import { GetEndpointMetadataListRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/metadata/get_metadata.gen'; import { GetEndpointSuggestionsRequestParamsInput, GetEndpointSuggestionsRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/endpoint/suggestions/get_suggestions.gen'; import { GetNotesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/timeline/get_notes/get_notes_route.gen'; -import { GetPolicyResponseRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/policy.gen'; +import { GetPolicyResponseRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/policy_response.gen'; import { GetProtectionUpdatesNoteRequestParamsInput } from '@kbn/security-solution-plugin/common/api/endpoint/protection_updates_note/protection_updates_note.gen'; import { GetRuleExecutionEventsRequestQueryInput, @@ -129,6 +144,16 @@ after 30 days. It also deletes other artifacts specific to the migration impleme .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + /** + * Ensures that the packages needed for prebuilt detection rules to work are installed and up to date + */ + bootstrapPrebuiltRules() { + return supertest + .post('/internal/detection_engine/prebuilt_rules/_bootstrap') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, /** * Create new detection rules in bulk. */ @@ -331,6 +356,114 @@ Migrations are initiated per index. While the process is neither destructive nor .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + /** + * Execute a given command on an endpoint + */ + endpointExecuteAction(props: EndpointExecuteActionProps) { + return supertest + .post('/api/endpoint/action/execute') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + /** + * Download a file from an endpoint + */ + endpointFileDownload(props: EndpointFileDownloadProps) { + return supertest + .get( + replaceParams( + '/api/endpoint/action/{action_id}/file/{file_id}/download`', + props.params + ) + ) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, + /** + * Get file info + */ + endpointFileInfo(props: EndpointFileInfoProps) { + return supertest + .get(replaceParams('/api/endpoint/action/{action_id}/file/{file_id}`', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, + /** + * Get action details + */ + endpointGetActionsDetails(props: EndpointGetActionsDetailsProps) { + return supertest + .get(replaceParams('/api/endpoint/action/{action_id}', props.params)) + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, + /** + * Get a list of action requests and their responses + */ + endpointGetActionsList(props: EndpointGetActionsListProps) { + return supertest + .get('/api/endpoint/action') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, + endpointGetActionsState() { + return supertest + .get('/api/endpoint/action/state') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, + /** + * Get action status + */ + endpointGetActionsStatus(props: EndpointGetActionsStatusProps) { + return supertest + .get('/api/endpoint/action_status') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, + /** + * Get a file from an endpoint + */ + endpointGetFileAction(props: EndpointGetFileActionProps) { + return supertest + .post('/api/endpoint/action/get_file') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + /** + * Get list of running processes on an endpoint + */ + endpointGetProcessesAction(props: EndpointGetProcessesActionProps) { + return supertest + .post('/api/endpoint/action/running_procs') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + /** + * Isolate an endpoint + */ + endpointIsolateAction(props: EndpointIsolateActionProps) { + return supertest + .post('/api/endpoint/action/isolate') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, endpointIsolateRedirect(props: EndpointIsolateRedirectProps) { return supertest .post('/api/endpoint/isolate') @@ -339,6 +472,50 @@ Migrations are initiated per index. While the process is neither destructive nor .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * Kill a running process on an endpoint + */ + endpointKillProcessAction(props: EndpointKillProcessActionProps) { + return supertest + .post('/api/endpoint/action/kill_process') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + /** + * Scan a file or directory + */ + endpointScanAction(props: EndpointScanActionProps) { + return supertest + .post('/api/endpoint/action/scan') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + /** + * Suspend a running process on an endpoint + */ + endpointSuspendProcessAction(props: EndpointSuspendProcessActionProps) { + return supertest + .post('/api/endpoint/action/suspend_process') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, + /** + * Release an endpoint + */ + endpointUnisolateAction(props: EndpointUnisolateActionProps) { + return supertest + .post('/api/endpoint/action/unisolate') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, endpointUnisolateRedirect(props: EndpointUnisolateRedirectProps) { return supertest .post('/api/endpoint/unisolate') @@ -347,6 +524,17 @@ Migrations are initiated per index. While the process is neither destructive nor .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * Upload a file to an endpoint + */ + endpointUploadAction(props: EndpointUploadActionProps) { + return supertest + .post('/api/endpoint/action/upload') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Export detection rules to an `.ndjson` file. The following configuration items are also included in the `.ndjson` file: - Actions @@ -437,6 +625,14 @@ finalize it. .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + getEndpointMetadataList(props: GetEndpointMetadataListProps) { + return supertest + .get('/api/endpoint/metadata') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, getEndpointSuggestions(props: GetEndpointSuggestionsProps) { return supertest .post(replaceParams('/api/endpoint/suggestions/{suggestion_type}', props.params)) @@ -889,12 +1085,54 @@ export interface DeleteTimelinesProps { export interface DeprecatedTriggerRiskScoreCalculationProps { body: DeprecatedTriggerRiskScoreCalculationRequestBodyInput; } +export interface EndpointExecuteActionProps { + body: EndpointExecuteActionRequestBodyInput; +} +export interface EndpointFileDownloadProps { + params: EndpointFileDownloadRequestParamsInput; +} +export interface EndpointFileInfoProps { + params: EndpointFileInfoRequestParamsInput; +} +export interface EndpointGetActionsDetailsProps { + params: EndpointGetActionsDetailsRequestParamsInput; +} +export interface EndpointGetActionsListProps { + query: EndpointGetActionsListRequestQueryInput; +} +export interface EndpointGetActionsStatusProps { + query: EndpointGetActionsStatusRequestQueryInput; +} +export interface EndpointGetFileActionProps { + body: EndpointGetFileActionRequestBodyInput; +} +export interface EndpointGetProcessesActionProps { + body: EndpointGetProcessesActionRequestBodyInput; +} +export interface EndpointIsolateActionProps { + body: EndpointIsolateActionRequestBodyInput; +} export interface EndpointIsolateRedirectProps { body: EndpointIsolateRedirectRequestBodyInput; } +export interface EndpointKillProcessActionProps { + body: EndpointKillProcessActionRequestBodyInput; +} +export interface EndpointScanActionProps { + body: EndpointScanActionRequestBodyInput; +} +export interface EndpointSuspendProcessActionProps { + body: EndpointSuspendProcessActionRequestBodyInput; +} +export interface EndpointUnisolateActionProps { + body: EndpointUnisolateActionRequestBodyInput; +} export interface EndpointUnisolateRedirectProps { body: EndpointUnisolateRedirectRequestBodyInput; } +export interface EndpointUploadActionProps { + body: EndpointUploadActionRequestBodyInput; +} export interface ExportRulesProps { query: ExportRulesRequestQueryInput; body: ExportRulesRequestBodyInput; @@ -921,6 +1159,9 @@ export interface GetAssetCriticalityRecordProps { export interface GetDraftTimelinesProps { query: GetDraftTimelinesRequestQueryInput; } +export interface GetEndpointMetadataListProps { + query: GetEndpointMetadataListRequestQueryInput; +} export interface GetEndpointSuggestionsProps { params: GetEndpointSuggestionsRequestParamsInput; body: GetEndpointSuggestionsRequestBodyInput; diff --git a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts index 54ae003de8698..5b80b5c7bc99d 100644 --- a/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts +++ b/x-pack/test/cloud_security_posture_api/routes/csp_benchmark_rules_bulk_update.ts @@ -484,8 +484,8 @@ export default function (providerContext: FtrProviderContext) { }); expect(status).to.be(403); }); - // Blocked by https://github.com/elastic/kibana/issues/188059 - it.skip('users with read privileges on cloud security should be able to mute', async () => { + + it('users with all privileges on cloud security should be able to mute', async () => { const rule1 = await getRandomCspBenchmarkRule(); const rule2 = await getRandomCspBenchmarkRule(); @@ -494,7 +494,7 @@ export default function (providerContext: FtrProviderContext) { .set(ELASTIC_HTTP_VERSION_HEADER, '1') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .set('kbn-xsrf', 'xxxx') - .auth('role_security_read_user', cspSecurity.getPasswordForUser('role_security_read_user')) + .auth('role_security_all_user', cspSecurity.getPasswordForUser('role_security_all_user')) .send({ action: 'mute', rules: [ diff --git a/x-pack/test/cloud_security_posture_api/routes/helper/user_roles_utilites.ts b/x-pack/test/cloud_security_posture_api/routes/helper/user_roles_utilites.ts index ef66b58d28311..aac6b19ddbef6 100644 --- a/x-pack/test/cloud_security_posture_api/routes/helper/user_roles_utilites.ts +++ b/x-pack/test/cloud_security_posture_api/routes/helper/user_roles_utilites.ts @@ -129,6 +129,24 @@ export function CspSecurityCommonProvider(providerContext: FtrProviderContext) { }, ], }, + { + name: 'role_security_all', + elasticsearch: { + indices: securityUserIndinces, + }, + kibana: [ + { + base: [], + feature: { + siem: ['all'], + fleet: ['all'], + fleetv2: ['all'], + savedObjectsManagement: ['all'], + }, + spaces: ['*'], + }, + ], + }, ]; const users = [ @@ -140,7 +158,7 @@ export function CspSecurityCommonProvider(providerContext: FtrProviderContext) { }, { name: 'role_security_read_user_alerts', - full_name: 'user with 0 security privilege for', + full_name: 'user with 0 security privilege', password: 'csp123', roles: ['role_security_read_alerts'], }, @@ -152,10 +170,16 @@ export function CspSecurityCommonProvider(providerContext: FtrProviderContext) { }, { name: 'role_security_no_read_user_alerts', - full_name: 'user with 0 security privilege for', + full_name: 'user with 0 security privilege', password: 'csp123', roles: ['role_security_no_read_alerts'], }, + { + name: 'role_security_all_user', + full_name: 'user with all security privilege', + password: 'csp123', + roles: ['role_security_all'], + }, ]; return { diff --git a/x-pack/test/cloud_security_posture_functional/agentless/create_agent.ts b/x-pack/test/cloud_security_posture_functional/agentless/create_agent.ts new file mode 100644 index 0000000000000..aa1a079262cba --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/agentless/create_agent.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CLOUD_CREDENTIALS_PACKAGE_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants'; +import * as http from 'http'; +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../ftr_provider_context'; +import { setupMockServer } from './mock_agentless_api'; +// eslint-disable-next-line import/no-default-export +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const mockAgentlessApiService = setupMockServer(); + const pageObjects = getPageObjects([ + 'common', + 'cspSecurity', + 'security', + 'header', + 'cisAddIntegration', + ]); + + const CIS_AWS_OPTION_TEST_ID = 'cisAwsTestId'; + + const AWS_SINGLE_ACCOUNT_TEST_ID = 'awsSingleTestId'; + + describe('Agentless cloud', function () { + let cisIntegration: typeof pageObjects.cisAddIntegration; + let mockApiServer: http.Server; + + before(async () => { + cisIntegration = pageObjects.cisAddIntegration; + mockApiServer = await mockAgentlessApiService.listen(8089); // Start the usage api mock server on port 8081 + }); + + after(async () => { + await await pageObjects.cspSecurity.logout(); + mockApiServer.close(); + }); + + it(`should create agentless-agent`, async () => { + const integrationPolicyName = `cloud_security_posture-${new Date().toISOString()}`; + await cisIntegration.navigateToAddIntegrationCspmWithVersionPage( + CLOUD_CREDENTIALS_PACKAGE_VERSION + ); + + await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID); + await cisIntegration.clickOptionButton(AWS_SINGLE_ACCOUNT_TEST_ID); + + await cisIntegration.inputIntegrationName(integrationPolicyName); + + await cisIntegration.selectSetupTechnology('agentless'); + await cisIntegration.selectAwsCredentials('direct'); + + await pageObjects.header.waitUntilLoadingHasFinished(); + + await cisIntegration.clickSaveButton(); + await pageObjects.header.waitUntilLoadingHasFinished(); + + await cisIntegration.navigateToIntegrationCspList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + + expect(await cisIntegration.getFirstCspmIntegrationPageIntegration()).to.be( + integrationPolicyName + ); + expect(await cisIntegration.getFirstCspmIntegrationPageAgent()).to.be( + `Agentless policy for ${integrationPolicyName}` + ); + }); + + it(`should create default agent-based agent`, async () => { + const integrationPolicyName = `cloud_security_posture-${new Date().toISOString()}`; + + await cisIntegration.navigateToAddIntegrationCspmWithVersionPage( + CLOUD_CREDENTIALS_PACKAGE_VERSION + ); + + await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID); + await cisIntegration.clickOptionButton(AWS_SINGLE_ACCOUNT_TEST_ID); + + await cisIntegration.inputIntegrationName(integrationPolicyName); + + await cisIntegration.clickSaveButton(); + await pageObjects.header.waitUntilLoadingHasFinished(); + + const agentPolicyName = await cisIntegration.getAgentBasedPolicyValue(); + + await cisIntegration.navigateToIntegrationCspList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + + expect(await cisIntegration.getFirstCspmIntegrationPageIntegration()).to.be( + integrationPolicyName + ); + expect(await cisIntegration.getFirstCspmIntegrationPageAgent()).to.be(agentPolicyName); + }); + }); +} diff --git a/x-pack/test/cloud_security_posture_functional/agentless/mock_agentless_api.ts b/x-pack/test/cloud_security_posture_functional/agentless/mock_agentless_api.ts new file mode 100644 index 0000000000000..129adde9c0018 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/agentless/mock_agentless_api.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createServer } from '@mswjs/http-middleware'; + +import { http, HttpResponse, StrictResponse } from 'msw'; + +export const setupMockServer = () => { + const server = createServer(deploymentHandler); + return server; +}; + +interface AgentlessApiResponse { + status: number; +} + +const deploymentHandler = http.post( + 'agentless-api/api/v1/ess/deployments', + async ({ request }): Promise<StrictResponse<AgentlessApiResponse>> => { + return HttpResponse.json({ + status: 200, + }); + } +); diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/benchmark_sanity.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/benchmark_sanity.ts index 277c1038e51ae..1dcbbac1d991e 100644 --- a/x-pack/test/cloud_security_posture_functional/cloud_tests/benchmark_sanity.ts +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/benchmark_sanity.ts @@ -34,14 +34,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const benchmarksRows = await benchmark.benchmarkPage.getBenchmarkTableRows(); for (const row of benchmarksRows) { const benchmarkName = await benchmark.benchmarkPage.getCisNameCellData(row); - const evaluated = await benchmark.benchmarkPage.getEvaluatedCellData(row); - const compliance = await benchmark.benchmarkPage.getComplianceCellData(row); - expect(await evaluated).to.not.contain( - 'Add', + const isEvaluationEmpty = await benchmark.benchmarkPage.isEvaluationEmpty(row); + const isComplianceEmpty = await benchmark.benchmarkPage.isComplianceEmpty(row); + + expect(isEvaluationEmpty).to.eql( + false, `The ${benchmarkName} does not have evaluated data` ); - expect(await compliance).to.not.contain( - 'No', + + expect(isComplianceEmpty).to.eql( + false, `The ${benchmarkName} does not have compliance data` ); } diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/dashboard_sanity.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/dashboard_sanity.ts index 6eaafcb154134..3808c3d843487 100644 --- a/x-pack/test/cloud_security_posture_functional/cloud_tests/dashboard_sanity.ts +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/dashboard_sanity.ts @@ -73,7 +73,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const findingsLinkCount = await dashboard.getFindingsLinksCount(TAB_TYPES.CLOUD); for (let i = 0; i < findingsLinkCount; i++) { const link = await dashboard.getFindingsLinkAtIndex(TAB_TYPES.CLOUD, i); - // for (const link of findingsLink) { await link.click(); await pageObjects.header.waitUntilLoadingHasFinished(); const groupSelector = await findings.groupSelector(); diff --git a/x-pack/test/cloud_security_posture_functional/config.agentless.ts b/x-pack/test/cloud_security_posture_functional/config.agentless.ts new file mode 100644 index 0000000000000..459c814b6e08c --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/config.agentless.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FtrConfigProviderContext } from '@kbn/test'; +import { CA_CERT_PATH, KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; +import { pageObjects } from './page_objects'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const xpackFunctionalConfig = await readConfigFile(require.resolve('./config.cloud.ts')); + // FTR configuration for cloud testing + return { + ...xpackFunctionalConfig.getAll(), + pageObjects, + junit: { + reportName: 'ESS Security Cloud Security Agentless Creating Agent Functional Tests', + }, + kbnTestServer: { + ...xpackFunctionalConfig.get('kbnTestServer'), + serverArgs: [ + ...xpackFunctionalConfig.get('kbnTestServer.serverArgs'), + `--xpack.fleet.agents.fleet_server.hosts=["https://ftr.kibana:8220"]`, + `--xpack.fleet.internal.fleetServerStandalone=true`, + `--xpack.fleet.enableExperimental.0=agentless`, + `--xpack.fleet.agentless.api.url=http://localhost:8089/agentless-api/api/v1/ess`, + `--xpack.fleet.agentless.api.tls.certificate=${KBN_CERT_PATH}`, + `--xpack.fleet.agentless.api.tls.key=${KBN_KEY_PATH}`, + `--xpack.fleet.agentless.api.tls.ca=${CA_CERT_PATH}`, + `--xpack.cloud.id=something-anything`, + ], + }, + // load tests in the index file + testFiles: [require.resolve('./agentless/create_agent.ts')], + }; +} diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts index 61b4c0d42a451..0c4581f21a829 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts @@ -354,11 +354,34 @@ export function AddCisIntegrationFormPageProvider({ await nameField[0].type(uuidv4()); }; + const inputIntegrationName = async (text: string) => { + const page = await testSubjects.find('createPackagePolicy_page'); + const nameField = await page.findAllByCssSelector('input[id="name"]'); + await nameField[0].clearValueWithKeyboard(); + await nameField[0].type(text); + }; + const getSecretComponentReplaceButton = async (secretButtonSelector: string) => { const secretComponentReplaceButton = await testSubjects.find(secretButtonSelector); return secretComponentReplaceButton; }; + const getFirstCspmIntegrationPageIntegration = async () => { + const integration = await testSubjects.find('integrationNameLink'); + return await integration.getVisibleText(); + }; + + const getFirstCspmIntegrationPageAgent = async () => { + const agent = await testSubjects.find('agentPolicyNameLink'); + // this is assuming that the agent was just created therefor should be the first element + return await agent.getVisibleText(); + }; + + const getAgentBasedPolicyValue = async () => { + const agentName = await testSubjects.find('createAgentPolicyNameField'); + return await agentName.getAttribute('value'); + }; + return { cisAzure, cisAws, @@ -397,5 +420,9 @@ export function AddCisIntegrationFormPageProvider({ getReplaceSecretButton, getSecretComponentReplaceButton, inputUniqueIntegrationName, + inputIntegrationName, + getFirstCspmIntegrationPageIntegration, + getFirstCspmIntegrationPageAgent, + getAgentBasedPolicyValue, }; } diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts index 1a6bf5877a74c..438f79dbc3332 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/benchmark_page.ts @@ -10,6 +10,7 @@ import { ELASTIC_HTTP_VERSION_HEADER, X_ELASTIC_INTERNAL_ORIGIN_REQUEST, } from '@kbn/core-http-common'; +import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services'; import type { FtrProviderContext } from '../ftr_provider_context'; export const CSP_BECNHMARK_TABLE = 'csp_benchmarks_table'; @@ -43,25 +44,50 @@ export function BenchmarkPagePageProvider({ getService, getPageObjects }: FtrPro getBenchmarkTableRows: async () => { const benchmarkTable = await testSubjects.find(CSP_BECNHMARK_TABLE); - return await benchmarkTable.findAllByXpath(`//tbody//tr`); + const tableRows = await benchmarkTable.findAllByXpath(`//tbody//tr`); + return tableRows; }, - getCellData: async (row: any, cellDataTestSubj: string) => { + getCellData: async (row: WebElementWrapper, cellDataTestSubj: string) => { const cell = await row.findByTestSubject(cellDataTestSubj); return await cell.getVisibleText(); }, - getEvaluatedCellData: async (row: any) => { + getEvaluatedCellData: async (row: WebElementWrapper) => { return await benchmarkPage.getCellData(row, 'benchmark-table-column-evaluated'); }, - getComplianceCellData: async (row: any) => { + getComplianceCellData: async (row: WebElementWrapper) => { return await benchmarkPage.getCellData(row, 'benchmark-table-column-compliance'); }, - getCisNameCellData: async (row: any) => { + getCisNameCellData: async (row: WebElementWrapper) => { return await benchmarkPage.getCellData(row, 'benchmark-table-column-cis-name'); }, + + isEvaluationEmpty: async (row: WebElementWrapper) => { + try { + const notEvaluated = await row.findAllByTestSubject('benchmark-not-evaluated-account', 200); + return notEvaluated.length > 0; + } catch (error) { + if (error.name === 'StaleElementReferenceError' || error.name === 'NoSuchElementError') { + return false; + } + throw error; + } + }, + + isComplianceEmpty: async (row: WebElementWrapper) => { + try { + const noCompliance = await row.findAllByTestSubject('benchmark-score-no-findings', 200); + return noCompliance.length > 0; + } catch (error) { + if (error.name === 'StaleElementReferenceError' || error.name === 'NoSuchElementError') { + return false; + } + throw error; + } + }, }; const navigateToBenchnmarkPage = async (space?: string) => { diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts index d7152037fceec..8827437a19332 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts @@ -105,7 +105,16 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv getFindingsLinks: async (tab: (typeof TAB_TYPES)[keyof typeof TAB_TYPES]) => { await dashboard.getDashoard(tab); const pageContainer = await testSubjects.find('pageContainer'); - return await pageContainer.findAllByXpath(`//button[contains(@class, 'euiLink')]`); + return [ + await pageContainer.findByTestSubject('dashboard-summary-passed-findings'), + await pageContainer.findByTestSubject('dashboard-summary-failed-findings'), + ...(await pageContainer.findAllByTestSubject('grouped-findings-evaluation-link')), + ...(await pageContainer.findAllByTestSubject('view-all-failed-findings')), + ...(await pageContainer.findAllByTestSubject('benchmark-section-bench-name')), + ...(await pageContainer.findAllByTestSubject('benchmark-asset-type')), + ...(await pageContainer.findAllByTestSubject('compliance-score-section-passed')), + ...(await pageContainer.findAllByTestSubject('compliance-score-section-failed')), + ]; }, getFindingsLinkAtIndex: async ( @@ -113,12 +122,12 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv linkIndex = 0 ) => { const allLinks = await dashboard.getFindingsLinks(tab); - return await allLinks[linkIndex]; + return allLinks[linkIndex]; }, getFindingsLinksCount: async (tab: (typeof TAB_TYPES)[keyof typeof TAB_TYPES]) => { const allLinks = await dashboard.getFindingsLinks(tab); - return await allLinks.length; + return allLinks.length; }, getIntegrationDashboardContainer: () => testSubjects.find('dashboard-container'), diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts b/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts index e225b7aab9d1a..26e146338a6e8 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/findings_alerts.ts @@ -123,7 +123,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const ruleName1 = data[0].rule.name; - describe('Findings Page - Alerts', function () { + // Failing: See https://github.com/elastic/kibana/issues/168991 + describe.skip('Findings Page - Alerts', function () { this.tags(['cloud_security_posture_findings_alerts']); let findings: typeof pageObjects.findings; let latestFindingsTable: typeof findings.latestFindingsTable; diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/get_rule_for_alert_testing.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/get_rule_for_alert_testing.ts index 5649031185feb..5c0500f89ef51 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/get_rule_for_alert_testing.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/get_rule_for_alert_testing.ts @@ -30,3 +30,18 @@ export const getRuleForAlertTesting = ( query: '*:*', from: '1900-01-01T00:00:00.000Z', }); + +export const getLuceneRuleForTesting = (): QueryRuleCreateProps => ({ + rule_id: 'lucene-rule-1', + enabled: true, + name: 'Incident 496 test rule', + description: 'Ensures lucene rules generate alerts', + risk_score: 1, + severity: 'high', + type: 'query', + index: ['auditbeat-*'], + query: + '((event.category: (network OR network_traffic) AND type: (tls OR http)) OR event.dataset: (network_traffic.tls OR network_traffic.http)) AND destination.domain:/[a-z]{3}.stage.[0-9]{8}..*/', + language: 'lucene', + from: '1900-01-01T00:00:00.000Z', +}); diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index 0de16bc6af3f4..29cfa9bb37e0b 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -173,7 +173,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); [ - { metric: 'cpuUsage', value: '0.8%' }, + { metric: 'cpuUsage', value: 'N/A' }, { metric: 'normalizedLoad1m', value: '1.4%' }, { metric: 'memoryUsage', value: '18.0%' }, { metric: 'diskUsage', value: '35.0%' }, @@ -406,7 +406,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHome.clearSearchTerm(); }); - it('sort nodes by descending value', async () => { + it.skip('sort nodes by descending value', async () => { await pageObjects.infraHome.goToTime(DATE_WITH_DATA); await pageObjects.infraHome.getWaffleMap(); await pageObjects.infraHome.sortNodesBy('value'); @@ -423,7 +423,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - it('sort nodes by ascending value', async () => { + it.skip('sort nodes by ascending value', async () => { await pageObjects.infraHome.goToTime(DATE_WITH_DATA); await pageObjects.infraHome.getWaffleMap(); await pageObjects.infraHome.sortNodesBy('value'); @@ -450,7 +450,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - it('filter nodes by search term', async () => { + it.skip('filter nodes by search term', async () => { await pageObjects.infraHome.goToTime(DATE_WITH_DATA); await pageObjects.infraHome.getWaffleMap(); await pageObjects.infraHome.enterSearchTerm('host.name: "demo-stack-apache-01"'); @@ -463,7 +463,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHome.clearSearchTerm(); }); - it('change color palette', async () => { + it.skip('change color palette', async () => { await pageObjects.infraHome.openLegendControls(); await pageObjects.infraHome.changePalette('temperature'); await pageObjects.infraHome.applyLegendControls(); @@ -581,14 +581,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'system.core.softirq.pct', 'system.core.steal.pct', 'system.cpu.nice.pct', - 'system.cpu.idle.pct', ]; for (const field of fields) { await pageObjects.infraHome.addCustomMetric(field); } const metricsCount = await pageObjects.infraHome.getMetricsContextMenuItemsCount(); - // there are 6 default metrics in the context menu for hosts + // there are 7 default metrics in the context menu for hosts expect(metricsCount).to.eql(20); await pageObjects.infraHome.ensureCustomMetricAddButtonIsDisabled(); diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index f185e479421e4..a016fbec64ecd 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -33,7 +33,7 @@ const tableEntries = [ { alertsCount: 2, title: 'demo-stack-apache-01', - cpuUsage: '1.2%', + cpuUsage: '0%', normalizedLoad: '0.5%', memoryUsage: '18.4%', memoryFree: '3.2 GB', @@ -44,7 +44,7 @@ const tableEntries = [ { alertsCount: 2, title: 'demo-stack-mysql-01', - cpuUsage: '0.9%', + cpuUsage: '0%', normalizedLoad: '0%', memoryUsage: '18.2%', memoryFree: '3.2 GB', @@ -55,7 +55,7 @@ const tableEntries = [ { alertsCount: 2, title: 'demo-stack-redis-01', - cpuUsage: '0.8%', + cpuUsage: '0%', normalizedLoad: '0%', memoryUsage: '15.9%', memoryFree: '3.3 GB', @@ -65,19 +65,19 @@ const tableEntries = [ }, { alertsCount: 0, - title: 'demo-stack-nginx-01', - cpuUsage: '0.8%', - normalizedLoad: '1.4%', - memoryUsage: '18%', - memoryFree: '3.2 GB', - diskSpaceUsage: '35%', + title: 'demo-stack-client-01', + cpuUsage: '0%', + normalizedLoad: '0.1%', + memoryUsage: '13.8%', + memoryFree: '3.3 GB', + diskSpaceUsage: '33.8%', rx: '0 bit/s', tx: '0 bit/s', }, { alertsCount: 0, title: 'demo-stack-haproxy-01', - cpuUsage: '0.8%', + cpuUsage: '0%', normalizedLoad: '0%', memoryUsage: '16.5%', memoryFree: '3.2 GB', @@ -87,12 +87,12 @@ const tableEntries = [ }, { alertsCount: 0, - title: 'demo-stack-client-01', - cpuUsage: '0.5%', - normalizedLoad: '0.1%', - memoryUsage: '13.8%', - memoryFree: '3.3 GB', - diskSpaceUsage: '33.8%', + title: 'demo-stack-nginx-01', + cpuUsage: '0%', + normalizedLoad: '1.4%', + memoryUsage: '18%', + memoryFree: '3.2 GB', + diskSpaceUsage: '35%', rx: '0 bit/s', tx: '0 bit/s', }, @@ -477,11 +477,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should render the computed metrics for each host entry', async () => { - hostRows.forEach((row, position) => { - pageObjects.infraHostsView - .getHostsRowData(row) - .then((hostRowData) => expect(hostRowData).to.eql(tableEntries[position])); - }); + for (let i = 0; i < hostRows.length; i++) { + const hostRowData = await pageObjects.infraHostsView.getHostsRowData(hostRows[i]); + expect(hostRowData).to.eql(tableEntries[i]); + } }); it('should select and filter hosts inside the table', async () => { @@ -550,7 +549,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('KPIs', () => { [ { metric: 'hostsCount', value: '6' }, - { metric: 'cpuUsage', value: '0.8%' }, + { metric: 'cpuUsage', value: 'N/A' }, { metric: 'normalizedLoad1m', value: '0.3%' }, { metric: 'memoryUsage', value: '16.8%' }, { metric: 'diskUsage', value: '35.7%' }, @@ -701,18 +700,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(hostRows.length).to.equal(3); - hostRows.forEach((row, position) => { - pageObjects.infraHostsView - .getHostsRowData(row) - .then((hostRowData) => expect(hostRowData).to.eql(filtererEntries[position])); - }); + for (let i = 0; i < hostRows.length; i++) { + const hostRowData = await pageObjects.infraHostsView.getHostsRowData(hostRows[i]); + expect(hostRowData).to.eql(filtererEntries[i]); + } }); it('should update the KPIs content on a search submit', async () => { await Promise.all( [ { metric: 'hostsCount', value: '3' }, - { metric: 'cpuUsage', value: '0.9%' }, + { metric: 'cpuUsage', value: 'N/A' }, { metric: 'normalizedLoad1m', value: '0.2%' }, { metric: 'memoryUsage', value: '17.5%' }, { metric: 'diskUsage', value: '35.7%' }, @@ -782,38 +780,38 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should show 5 rows on the first page', async () => { const hostRows = await pageObjects.infraHostsView.getHostsTableData(); - hostRows.forEach((row, position) => { - pageObjects.infraHostsView - .getHostsRowData(row) - .then((hostRowData) => expect(hostRowData).to.eql(tableEntries[position])); - }); + + for (let i = 0; i < hostRows.length; i++) { + const hostRowData = await pageObjects.infraHostsView.getHostsRowData(hostRows[i]); + expect(hostRowData).to.eql(tableEntries[i]); + } }); it('should paginate to the last page', async () => { await pageObjects.infraHostsView.paginateTo(2); const hostRows = await pageObjects.infraHostsView.getHostsTableData(); - hostRows.forEach((row) => { - pageObjects.infraHostsView - .getHostsRowData(row) - .then((hostRowData) => expect(hostRowData).to.eql(tableEntries[5])); - }); + + expect(hostRows.length).to.equal(1); + + const hostRowData = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); + expect(hostRowData).to.eql(tableEntries[5]); }); it('should show all hosts on the same page', async () => { await pageObjects.infraHostsView.changePageSize(10); const hostRows = await pageObjects.infraHostsView.getHostsTableData(); - hostRows.forEach((row, position) => { - pageObjects.infraHostsView - .getHostsRowData(row) - .then((hostRowData) => expect(hostRowData).to.eql(tableEntries[position])); - }); + + for (let i = 0; i < hostRows.length; i++) { + const hostRowData = await pageObjects.infraHostsView.getHostsRowData(hostRows[i]); + expect(hostRowData).to.eql(tableEntries[i]); + } }); it('should sort by a numeric field asc', async () => { - await pageObjects.infraHostsView.sortByCpuUsage(); + await pageObjects.infraHostsView.sortByMemoryUsage(); let hostRows = await pageObjects.infraHostsView.getHostsTableData(); const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); - expect(hostDataFirtPage).to.eql(tableEntries[5]); + expect(hostDataFirtPage).to.eql(tableEntries[3]); await pageObjects.infraHostsView.paginateTo(2); hostRows = await pageObjects.infraHostsView.getHostsTableData(); @@ -822,7 +820,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should sort by a numeric field desc', async () => { - await pageObjects.infraHostsView.sortByCpuUsage(); + await pageObjects.infraHostsView.sortByMemoryUsage(); let hostRows = await pageObjects.infraHostsView.getHostsTableData(); const hostDataFirtPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); expect(hostDataFirtPage).to.eql(tableEntries[0]); @@ -830,7 +828,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.infraHostsView.paginateTo(2); hostRows = await pageObjects.infraHostsView.getHostsTableData(); const hostDataLastPage = await pageObjects.infraHostsView.getHostsRowData(hostRows[0]); - expect(hostDataLastPage).to.eql(tableEntries[5]); + expect(hostDataLastPage).to.eql(tableEntries[3]); }); it('should sort by text field asc', async () => { diff --git a/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts b/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts index 358095e4350b8..22ed2bd035ee1 100644 --- a/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/logs/logs_source_configuration.ts @@ -76,6 +76,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.header.waitUntilLoadingHasFinished(); + await infraSourceConfigurationForm.selectIndicesPanel(); + const nameInput = await infraSourceConfigurationForm.getNameInput(); await nameInput.clearValueWithKeyboard({ charByChar: true }); await nameInput.type('Modified Source'); diff --git a/x-pack/test/functional/apps/infra/node_details.ts b/x-pack/test/functional/apps/infra/node_details.ts index bba769b6b9764..fbc442b5079c0 100644 --- a/x-pack/test/functional/apps/infra/node_details.ts +++ b/x-pack/test/functional/apps/infra/node_details.ts @@ -558,7 +558,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); [ - { metric: 'cpuUsage', value: '99.6%' }, + { metric: 'cpuUsage', value: '100.0%' }, { metric: 'normalizedLoad1m', value: '1,300.3%' }, { metric: 'memoryUsage', value: '42.2%' }, { metric: 'diskUsage', value: '36.0%' }, diff --git a/x-pack/test/functional/apps/lens/group2/fields_list.ts b/x-pack/test/functional/apps/lens/group2/fields_list.ts index 14151f481913f..dff265f0c4a40 100644 --- a/x-pack/test/functional/apps/lens/group2/fields_list.ts +++ b/x-pack/test/functional/apps/lens/group2/fields_list.ts @@ -47,6 +47,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await fieldEditor.typeScript("emit('abc')"); await fieldEditor.save(); await PageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.missingOrFail('fieldEditor'); }); }); it('should show all fields as available', async () => { diff --git a/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts b/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts index b944ebd9b8306..3c87d53031aa4 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts @@ -85,7 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('render content virtual column properly', async () => { it('should render log level and log message when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); expect(cellValue.includes('A sample log')).to.be(true); @@ -94,7 +94,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render log message when present and skip log level when missing', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(1, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(1, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(false); expect(cellValue.includes('A sample log')).to.be(true); @@ -103,7 +103,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render message from error object when top level message not present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(2, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(2, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); expect(cellValue.includes('error.message')).to.be(true); @@ -113,7 +113,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render message from event.original when top level message and error.message not present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(3, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(3, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); expect(cellValue.includes('event.original')).to.be(true); @@ -123,7 +123,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the whole JSON when neither message, error.message and event.original are present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(4, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(4, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); @@ -137,7 +137,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('on cell expansion with no message field should open JSON Viewer', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - await dataGrid.clickCellExpandButton(4, 4); + await dataGrid.clickCellExpandButtonExcludingControlColumns(4, 2); await testSubjects.existOrFail('dataTableExpandCellActionJsonPopover'); }); }); @@ -145,7 +145,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('on cell expansion with message field should open regular popover', async () => { await navigateToLogsExplorer(); await retry.tryForTime(TEST_TIMEOUT, async () => { - await dataGrid.clickCellExpandButton(3, 4); + await dataGrid.clickCellExpandButtonExcludingControlColumns(3, 2); await testSubjects.existOrFail('euiDataGridExpansionPopover'); }); }); @@ -154,7 +154,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('render resource virtual column properly', async () => { it('should render service name and host name when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 3); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('synth-service')).to.be(true); expect(cellValue.includes('synth-host')).to.be(true); @@ -168,7 +168,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should render a popover with cell actions when a chip on content column is clicked', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const logLevelChip = await cellElement.findByTestSubject('*logLevelBadge-'); await logLevelChip.click(); // Check Filter In button is present @@ -182,7 +182,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the table filtered where log.level value is info when filter in action is clicked', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const logLevelChip = await cellElement.findByTestSubject('*logLevelBadge-'); const actionSelector = 'dataTableCellAction_addToFilterAction_log.level'; @@ -203,7 +203,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the table filtered where log.level value is not info when filter out action is clicked', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const logLevelChip = await cellElement.findByTestSubject('*logLevelBadge-'); const actionSelector = 'dataTableCellAction_removeFromFilterAction_log.level'; @@ -222,7 +222,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the table filtered where service.name value is selected', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 3); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); const serviceNameChip = await cellElement.findByTestSubject( 'dataTablePopoverChip_service.name' ); diff --git a/x-pack/test/functional/apps/observability_logs_explorer/custom_control_columns.ts b/x-pack/test/functional/apps/observability_logs_explorer/custom_control_columns.ts index 21dd8a6772bb7..58b123d08cdaf 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/custom_control_columns.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/custom_control_columns.ts @@ -47,7 +47,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render control column with proper header', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { // First control column has no title, so empty string, leading control column has title - expect(await dataGrid.getControlColumnHeaderFields()).to.eql(['', 'actions']); + expect(await dataGrid.getControlColumnHeaderFields()).to.eql(['', '', '', '']); }); }); @@ -61,7 +61,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the degraded icon in the leading control column if degraded doc exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(1, 1); + const cellElement = await dataGrid.getCellElement(1, 2); const degradedButton = await cellElement.findByTestSubject('docTableDegradedDocExist'); expect(degradedButton).to.not.be.empty(); }); @@ -69,7 +69,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the disabled degraded icon in the leading control column when degraded doc does not exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 1); + const cellElement = await dataGrid.getCellElement(0, 2); const degradedDisableButton = await cellElement.findByTestSubject( 'docTableDegradedDocDoesNotExist' ); @@ -79,7 +79,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the stacktrace icon in the leading control column when stacktrace exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(4, 1); + const cellElement = await dataGrid.getCellElement(4, 3); const stacktraceButton = await cellElement.findByTestSubject('docTableStacktraceExist'); expect(stacktraceButton).to.not.be.empty(); }); @@ -87,7 +87,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the stacktrace icon disabled in the leading control column when stacktrace does not exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(1, 1); + const cellElement = await dataGrid.getCellElement(1, 3); const stacktraceButton = await cellElement.findByTestSubject( 'docTableStacktraceDoesNotExist' ); diff --git a/x-pack/test/functional/config.base.js b/x-pack/test/functional/config.base.js index 56ecbef55759f..4fdb988fef098 100644 --- a/x-pack/test/functional/config.base.js +++ b/x-pack/test/functional/config.base.js @@ -185,6 +185,15 @@ export default async function ({ readConfigFile }) { maintenanceWindows: { pathname: '/app/management/insightsAndAlerting/maintenanceWindows', }, + obsAIAssistant: { + pathname: '/app/observabilityAIAssistant', + }, + aiAssistantManagementSelection: { + pathname: '/app/management/kibana/aiAssistantManagementSelection', + }, + obsAIAssistantManagement: { + pathname: '/app/management/kibana/observabilityAiAssistantManagement', + }, }, suiteTags: { diff --git a/x-pack/test/functional/es_archives/auditbeat/hosts/data.json.gz b/x-pack/test/functional/es_archives/auditbeat/hosts/data.json.gz index 00c6963b16937..b400db7f4540d 100644 Binary files a/x-pack/test/functional/es_archives/auditbeat/hosts/data.json.gz and b/x-pack/test/functional/es_archives/auditbeat/hosts/data.json.gz differ diff --git a/x-pack/test/functional/page_objects/infra_home_page.ts b/x-pack/test/functional/page_objects/infra_home_page.ts index 03995ac6e7a6d..ec7908d916e9b 100644 --- a/x-pack/test/functional/page_objects/infra_home_page.ts +++ b/x-pack/test/functional/page_objects/infra_home_page.ts @@ -94,7 +94,7 @@ export function InfraHomePageProvider({ getService, getPageObjects }: FtrProvide async clickOnFirstNode() { const firstNode = await this.getFirstNode(); - firstNode.click(); + return firstNode.click(); }, async clickOnGoToNodeDetails() { diff --git a/x-pack/test/functional/page_objects/infra_hosts_view.ts b/x-pack/test/functional/page_objects/infra_hosts_view.ts index 2ee8bf266f843..d92cf51892a5f 100644 --- a/x-pack/test/functional/page_objects/infra_hosts_view.ts +++ b/x-pack/test/functional/page_objects/infra_hosts_view.ts @@ -247,17 +247,17 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) { }, // Sorting - getCpuUsageHeader() { - return testSubjects.find('tableHeaderCell_cpu_3'); + getMemoryHeader() { + return testSubjects.find('tableHeaderCell_memory_5'); }, getTitleHeader() { return testSubjects.find('tableHeaderCell_title_2'); }, - async sortByCpuUsage() { - const diskLatency = await this.getCpuUsageHeader(); - const button = await testSubjects.findDescendant('tableHeaderSortButton', diskLatency); + async sortByMemoryUsage() { + const memory = await this.getMemoryHeader(); + const button = await testSubjects.findDescendant('tableHeaderSortButton', memory); await button.click(); }, diff --git a/x-pack/test/functional/services/infra_source_configuration_form.ts b/x-pack/test/functional/services/infra_source_configuration_form.ts index da39347c36389..c8d28e0e3656b 100644 --- a/x-pack/test/functional/services/infra_source_configuration_form.ts +++ b/x-pack/test/functional/services/infra_source_configuration_form.ts @@ -30,7 +30,9 @@ export function InfraSourceConfigurationFormProvider({ async getMetricIndicesInput(): Promise<WebElementWrapper> { return await testSubjects.findDescendant('~metricIndicesInput', await this.getForm()); }, - + async selectIndicesPanel(): Promise<void> { + return await testSubjects.click('logIndicesCheckableCard'); + }, /** * Logs */ diff --git a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts index 300aa0fb0a8a1..314bc202a6268 100644 --- a/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts +++ b/x-pack/test/functional_with_es_ssl/apps/discover_ml_uptime/discover/search_source_alert.ts @@ -324,7 +324,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await titleElem.getAttribute('value')).to.equal(dataView); }; - describe('Search source Alert', () => { + // FLAKY: https://github.com/elastic/kibana/issues/190090 + describe.skip('Search source Alert', () => { before(async () => { await security.testUser.setRoles(['discover_alert']); diff --git a/x-pack/test/observability_ai_assistant_functional/common/connectors.ts b/x-pack/test/observability_ai_assistant_functional/common/connectors.ts new file mode 100644 index 0000000000000..0930c1e4ff7c4 --- /dev/null +++ b/x-pack/test/observability_ai_assistant_functional/common/connectors.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 { Agent as SuperTestAgent } from 'supertest'; +import { LlmProxy } from '../../observability_ai_assistant_api_integration/common/create_llm_proxy'; +export async function createConnector(proxy: LlmProxy, supertest: SuperTestAgent) { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'foo', + config: { + apiProvider: 'OpenAI', + apiUrl: `http://localhost:${proxy.getPort()}`, + defaultModel: 'gpt-4', + }, + secrets: { apiKey: 'myApiKey' }, + connector_type_id: '.gen-ai', + }) + .expect(200); +} + +export async function deleteConnectors(supertest: SuperTestAgent) { + const connectors = await supertest.get('/api/actions/connectors').expect(200); + const promises = connectors.body.map((connector: { id: string }) => { + return supertest + .delete(`/api/actions/connector/${connector.id}`) + .set('kbn-xsrf', 'foo') + .expect(204); + }); + + return Promise.all(promises); +} diff --git a/x-pack/test/observability_ai_assistant_functional/common/ui/index.ts b/x-pack/test/observability_ai_assistant_functional/common/ui/index.ts index d0a45f61e17be..a5d2802dfbcc5 100644 --- a/x-pack/test/observability_ai_assistant_functional/common/ui/index.ts +++ b/x-pack/test/observability_ai_assistant_functional/common/ui/index.ts @@ -33,6 +33,8 @@ const pages = { retryButton: 'observabilityAiAssistantWelcomeMessageSetUpKnowledgeBaseButton', conversationLink: 'observabilityAiAssistantConversationsLink', positiveFeedbackButton: 'observabilityAiAssistantFeedbackButtonsPositiveButton', + connectorsErrorMsg: 'observabilityAiAssistantConnectorsError', + conversationsPage: 'observabilityAiAssistantConversationsPage', }, createConnectorFlyout: { flyout: 'create-connector-flyout', @@ -48,6 +50,18 @@ const pages = { button: 'obsAiAssistantInsightButton', text: 'obsAiAssistantInsightResponse', }, + links: { + solutionMenuLink: 'observability-nav-observabilityAIAssistant-ai_assistant', + globalHeaderButton: 'observabilityAiAssistantAppNavControlButton', + }, + settings: { + settingsPage: 'aiAssistantSettingsPage', + managementLink: 'aiAssistantManagementSelection', + logsIndexPatternInput: + 'management-settings-editField-observability:aiAssistantLogsIndexPattern', + saveButton: 'observabilityAiAssistantManagementBottomBarActionsButton', + aiAssistantCard: 'aiAssistantSelectionPageObservabilityCard', + }, }; export async function ObservabilityAIAssistantUIProvider({ diff --git a/x-pack/test/observability_ai_assistant_functional/tests/contextual_insights/index.spec.ts b/x-pack/test/observability_ai_assistant_functional/tests/contextual_insights/index.spec.ts index 41ad6f793ee93..7355b508ce5a0 100644 --- a/x-pack/test/observability_ai_assistant_functional/tests/contextual_insights/index.spec.ts +++ b/x-pack/test/observability_ai_assistant_functional/tests/contextual_insights/index.spec.ts @@ -14,6 +14,7 @@ import { LlmProxy, } from '../../../observability_ai_assistant_api_integration/common/create_llm_proxy'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { deleteConnectors, createConnector } from '../../common/connectors'; export default function ApiTest({ getService, getPageObjects }: FtrProviderContext) { const ui = getService('observabilityAIAssistantUI'); @@ -60,35 +61,6 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte await apmSynthtraceEsClient.index(documents); } - async function createConnector(proxy: LlmProxy) { - await supertest - .post('/api/actions/connector') - .set('kbn-xsrf', 'foo') - .send({ - name: 'foo', - config: { - apiProvider: 'OpenAI', - apiUrl: `http://localhost:${proxy.getPort()}`, - defaultModel: 'gpt-4', - }, - secrets: { apiKey: 'myApiKey' }, - connector_type_id: '.gen-ai', - }) - .expect(200); - } - - async function deleteConnectors() { - const connectors = await supertest.get('/api/actions/connectors').expect(200); - const promises = connectors.body.map((connector: { id: string }) => { - return supertest - .delete(`/api/actions/connector/${connector.id}`) - .set('kbn-xsrf', 'foo') - .expect(204); - }); - - return Promise.all(promises); - } - async function navigateToError() { await common.navigateToUrl('apm', 'services/opbeans-go/errors/some-expection-key', { shouldUseHashForSubUrl: false, @@ -111,7 +83,7 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte describe('Contextual insights for APM errors', () => { before(async () => { await Promise.all([ - deleteConnectors(), // cleanup previous connectors + deleteConnectors(supertest), // cleanup previous connectors apmSynthtraceEsClient.clean(), // cleanup previous synthtrace data ]); @@ -123,7 +95,7 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte after(async () => { await Promise.all([ - deleteConnectors(), // cleanup previous connectors + deleteConnectors(supertest), // cleanup previous connectors apmSynthtraceEsClient.clean(), // cleanup synthtrace data ui.auth.logout(), // logout ]); @@ -141,7 +113,7 @@ export default function ApiTest({ getService, getPageObjects }: FtrProviderConte before(async () => { proxy = await createLlmProxy(log); - await createConnector(proxy); + await createConnector(proxy, supertest); }); after(async () => { diff --git a/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/assistant_security.spec.ts b/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/assistant_security.spec.ts new file mode 100644 index 0000000000000..1108c7251b89d --- /dev/null +++ b/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/assistant_security.spec.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { + createLlmProxy, + LlmProxy, +} from '../../../observability_ai_assistant_api_integration/common/create_llm_proxy'; +import { createConnector, deleteConnectors } from '../../common/connectors'; +import { createAndLoginUserWithCustomRole, deleteAndLogoutUser } from './helpers'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const log = getService('log'); + const supertest = getService('supertest'); + const PageObjects = getPageObjects(['common', 'error', 'navigationalSearch', 'security']); + const ui = getService('observabilityAIAssistantUI'); + const testSubjects = getService('testSubjects'); + + describe('ai assistant privileges', () => { + describe('all privileges', () => { + before(async () => { + await createAndLoginUserWithCustomRole(getPageObjects, getService, { + // need some obs app or obs menu wont show where we can click on AI Assistant + infrastructure: ['all'], + observabilityAIAssistant: ['all'], + // requires connectors to chat + actions: ['read'], + }); + }); + + after(async () => { + await deleteAndLogoutUser(getService, getPageObjects); + }); + + it('shows AI Assistant link in solution nav', async () => { + // navigate to an observability app so the left side o11y menu shows up + await PageObjects.common.navigateToUrl('infraOps', '', { + ensureCurrentUrl: true, + shouldLoginIfPrompted: false, + }); + await testSubjects.existOrFail(ui.pages.links.solutionMenuLink); + }); + it('shows AI Assistant button in global nav', async () => { + await testSubjects.existOrFail(ui.pages.links.globalHeaderButton); + }); + it('shows AI Assistant conversations link in search', async () => { + await PageObjects.navigationalSearch.searchFor('observability ai assistant'); + const results = await PageObjects.navigationalSearch.getDisplayedResults(); + expect(results[0].label).to.eql('Observability AI Assistant / Conversations'); + }); + describe('with no connector setup', () => { + before(async () => { + await deleteConnectors(supertest); + }); + it('loads conversations UI with setup connector message', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistant', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.conversations.setupGenAiConnectorsButtonSelector); + }); + }); + describe('with connector setup', () => { + let proxy: LlmProxy; + + before(async () => { + await deleteConnectors(supertest); + proxy = await createLlmProxy(log); + await createConnector(proxy, supertest); + }); + + after(async () => { + proxy.close(); + await deleteConnectors(supertest); + }); + it('loads conversations UI with ability to chat', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistant', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + const chatInputElement = await testSubjects.find(ui.pages.conversations.chatInput); + await testSubjects.existOrFail(ui.pages.conversations.chatInput); + const isDisabled = await chatInputElement.getAttribute('disabled'); + expect(isDisabled).to.be(null); + }); + }); + }); + describe('no actions privileges', () => { + before(async () => { + await createAndLoginUserWithCustomRole(getPageObjects, getService, { + // need some obs app or obs menu wont show where we can click on AI Assistant + infrastructure: ['all'], + observabilityAIAssistant: ['all'], + }); + }); + it('loads conversations UI with connector error message', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistant', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.conversations.connectorsErrorMsg); + }); + after(async () => { + await deleteAndLogoutUser(getService, getPageObjects); + }); + }); + describe('no privileges', () => { + before(async () => { + await createAndLoginUserWithCustomRole(getPageObjects, getService, { + // need some obs app or obs menu wont show where we can click on AI Assistant + infrastructure: ['all'], + }); + }); + it('shows no AI Assistant link in solution nav', async () => { + // navigate to an observability app so the left side o11y menu shows up + await PageObjects.common.navigateToUrl('infraOps', '', { + ensureCurrentUrl: true, + shouldLoginIfPrompted: false, + }); + await testSubjects.missingOrFail(ui.pages.links.solutionMenuLink); + }); + it('shows no AI Assistant button in global nav', async () => { + await testSubjects.missingOrFail(ui.pages.links.globalHeaderButton); + }); + it('shows no AI Assistant conversations link in global search', async () => { + await PageObjects.navigationalSearch.searchFor('observability ai assistant'); + const results = await PageObjects.navigationalSearch.getDisplayedResults(); + expect(results.length).to.eql(0); + }); + it('cannot navigate to AI Assistant page', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistant', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.missingOrFail(ui.pages.conversations.conversationsPage); + }); + after(async () => { + await deleteAndLogoutUser(getService, getPageObjects); + }); + }); + }); +} diff --git a/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/helpers.ts b/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/helpers.ts new file mode 100644 index 0000000000000..206b5d0df78f7 --- /dev/null +++ b/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/helpers.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 { InheritedFtrProviderContext } from '../../ftr_provider_context'; + +const AI_ASSISTANT_ROLE_NAME = 'ai_assistant_role'; +const AI_ASSISTANT_USER_NAME = 'ai_assistant_user'; +const AI_ASSISTANT_USER_PASSWORD = `${AI_ASSISTANT_USER_NAME}-password`; + +export const createAndLoginUserWithCustomRole = async ( + getPageObjects: InheritedFtrProviderContext['getPageObjects'], + getService: InheritedFtrProviderContext['getService'], + featurePrivileges: { [key: string]: string[] } +) => { + const security = getService('security'); + const PageObjects = getPageObjects(['security']); + + const kibanaPrivileges = [ + { + feature: featurePrivileges, + spaces: ['*'], + }, + ]; + + await security.role.create(AI_ASSISTANT_ROLE_NAME, { + kibana: kibanaPrivileges, + }); + + await security.user.create(AI_ASSISTANT_USER_NAME, { + password: AI_ASSISTANT_USER_PASSWORD, + roles: [AI_ASSISTANT_ROLE_NAME], + full_name: 'test user', + }); + + await PageObjects.security.forceLogout(); + + await PageObjects.security.login(AI_ASSISTANT_USER_NAME, AI_ASSISTANT_USER_PASSWORD, { + expectSpaceSelector: false, + }); +}; + +export const deleteAndLogoutUser = async ( + getService: InheritedFtrProviderContext['getService'], + getPageObjects: InheritedFtrProviderContext['getPageObjects'] +) => { + const security = getService('security'); + const PageObjects = getPageObjects(['security']); + + await PageObjects.security.forceLogout(); + await Promise.all([ + security.role.delete(AI_ASSISTANT_ROLE_NAME), + security.user.delete(AI_ASSISTANT_USER_NAME), + ]); +}; diff --git a/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/settings_security.spec.ts b/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/settings_security.spec.ts new file mode 100644 index 0000000000000..cea40d3ad10ce --- /dev/null +++ b/x-pack/test/observability_ai_assistant_functional/tests/feature_controls/settings_security.spec.ts @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { interceptRequest } from '../../common/intercept_request'; +import { createAndLoginUserWithCustomRole, deleteAndLogoutUser } from './helpers'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const browser = getService('browser'); + const PageObjects = getPageObjects(['common', 'error', 'navigationalSearch', 'security']); + const ui = getService('observabilityAIAssistantUI'); + const testSubjects = getService('testSubjects'); + const driver = getService('__webdriver__'); + const retry = getService('retry'); + const toasts = getService('toasts'); + + describe('ai assistant management privileges', () => { + describe('all privileges', () => { + before(async () => { + await createAndLoginUserWithCustomRole(getPageObjects, getService, { + // we need all these privileges to view and modify Obs AI Assistant settings view + observabilityAIAssistant: ['all'], + // aiAssistantManagementSelection determines link visibility in stack management and navigating to the page + // but not whether you can read/write the settings + aiAssistantManagementSelection: ['all'], + // advancedSettings determines whether user can read/write the settings + advancedSettings: ['all'], + }); + }); + + after(async () => { + await deleteAndLogoutUser(getService, getPageObjects); + }); + + it('shows AI Assistant settings link in solution nav', async () => { + await PageObjects.common.navigateToUrl('management', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.settings.managementLink); + }); + + it('allows access to ai assistant settings landing page', async () => { + await PageObjects.common.navigateToUrl('aiAssistantManagementSelection', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + }); + it('allows access to obs ai assistant settings view', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistantManagement', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.settings.settingsPage); + }); + it('allows updating of an advanced setting', async () => { + const testLogsIndexPattern = 'my-logs-index-pattern'; + const logsIndexPatternInput = await testSubjects.find( + ui.pages.settings.logsIndexPatternInput + ); + await logsIndexPatternInput.clearValue(); + await logsIndexPatternInput.type(testLogsIndexPattern); + const saveButton = await testSubjects.find(ui.pages.settings.saveButton); + await saveButton.click(); + await browser.refresh(); + const logsIndexPatternInputValue = await logsIndexPatternInput.getAttribute('value'); + expect(logsIndexPatternInputValue).to.be(testLogsIndexPattern); + // reset the value + await logsIndexPatternInput.clearValue(); + await logsIndexPatternInput.type('logs-*'); + await saveButton.click(); + }); + it('displays failure toast on failed request', async () => { + const logsIndexPatternInput = await testSubjects.find( + ui.pages.settings.logsIndexPatternInput + ); + // Wait until the input has the default value 'logs-*' to prevent flakiness + await retry.waitFor('input field to have default value', async () => { + const value = await logsIndexPatternInput.getAttribute('value'); + return value === 'logs-*'; + }); + await logsIndexPatternInput.clearValue(); + await logsIndexPatternInput.type('test'); + + await interceptRequest( + driver.driver, + '*kibana\\/settings*', + (responseFactory) => { + return responseFactory.fail(); + }, + async () => { + await testSubjects.click(ui.pages.settings.saveButton); + } + ); + + await retry.waitFor('Error saving settings toast', async () => { + const count = await toasts.getCount(); + return count > 0; + }); + }); + }); + describe('with advancedSettings read privilege', () => { + before(async () => { + await createAndLoginUserWithCustomRole(getPageObjects, getService, { + observabilityAIAssistant: ['all'], + aiAssistantManagementSelection: ['all'], + advancedSettings: ['read'], + }); + }); + + after(async () => { + await deleteAndLogoutUser(getService, getPageObjects); + }); + + it('shows AI Assistant settings link in solution nav', async () => { + await PageObjects.common.navigateToUrl('management', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.settings.managementLink); + }); + + it('allows access to ai assistant settings landing page', async () => { + await PageObjects.common.navigateToUrl('aiAssistantManagementSelection', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.settings.aiAssistantCard); + }); + it('allows access to obs ai assistant settings page', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistantManagement', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.settings.settingsPage); + }); + it('has disabled inputs', async () => { + const logsIndexPatternInput = await testSubjects.find( + ui.pages.settings.logsIndexPatternInput + ); + expect(await logsIndexPatternInput.getAttribute('disabled')).to.be('true'); + }); + }); + describe('observabilityAIAssistant privilege with no aiAssistantManagementSelection privilege', () => { + before(async () => { + await createAndLoginUserWithCustomRole(getPageObjects, getService, { + // we need at least one feature available to login + observabilityAIAssistant: ['all'], + }); + }); + + after(async () => { + await deleteAndLogoutUser(getService, getPageObjects); + }); + + it('does not show AI Assistant settings link in solution nav', async () => { + await PageObjects.common.navigateToUrl('management', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.missingOrFail(ui.pages.settings.managementLink); + }); + + it('does not allow access to ai assistant settings landing page', async () => { + await PageObjects.common.navigateToUrl('aiAssistantManagementSelection', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.missingOrFail(ui.pages.settings.aiAssistantCard); + }); + it('allows access to obs ai assistant settings page', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistantManagement', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.missingOrFail(ui.pages.settings.settingsPage); + }); + }); + describe('aiAssistantManagementSelection privilege with no observabilityAIAssistant privilege', () => { + before(async () => { + await createAndLoginUserWithCustomRole(getPageObjects, getService, { + aiAssistantManagementSelection: ['all'], + advancedSettings: ['all'], + }); + }); + + after(async () => { + await deleteAndLogoutUser(getService, getPageObjects); + }); + + it('shows AI Assistant settings link in solution nav', async () => { + await PageObjects.common.navigateToUrl('management', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.settings.managementLink); + }); + + it('allows access to ai assistant settings landing page', async () => { + await PageObjects.common.navigateToUrl('aiAssistantManagementSelection', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.existOrFail(ui.pages.settings.aiAssistantCard); + }); + it('does not allow access to obs ai assistant settings page', async () => { + await PageObjects.common.navigateToUrl('obsAIAssistantManagement', '', { + ensureCurrentUrl: false, + shouldLoginIfPrompted: false, + shouldUseHashForSubUrl: false, + }); + await testSubjects.missingOrFail(ui.pages.settings.settingsPage); + }); + }); + }); +} diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts index 066a004df3814..44d2257f8a957 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/health_route.ts @@ -140,7 +140,12 @@ export default function ({ getService }: FtrProviderContext) { }, }, request_capacity: 1000, - max_workers: 10, + capacity: { + config: 10, + as_workers: 10, + as_cost: 20, + }, + claim_strategy: 'default', }); }); diff --git a/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts index 5a4a71c6abb2d..67abb9d5f4e08 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts @@ -75,7 +75,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('creating', () => { + // FLAKY: https://github.com/elastic/kibana/issues/160583 + describe.skip('creating', () => { beforeEach(async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.gotoDashboardLandingPage(); diff --git a/x-pack/test/scalability/config.ts b/x-pack/test/scalability/config.ts index ebfa91d4ce1e2..3315276a688ca 100644 --- a/x-pack/test/scalability/config.ts +++ b/x-pack/test/scalability/config.ts @@ -11,8 +11,8 @@ import path from 'path'; // @ts-expect-error we have to check types with "allowJs: false" for now, causing this import to fail import { REPO_ROOT } from '@kbn/repo-info'; import { createFlagError } from '@kbn/dev-cli-errors'; -import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; import { v4 as uuidV4 } from 'uuid'; +import { services } from './services'; import { ScalabilityTestRunner } from './runner'; import { FtrProviderContext } from './ftr_provider_context'; import { ScalabilityJourney } from './types'; @@ -49,7 +49,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...baseConfig, - services: commonFunctionalServices, + services, pageObjects: {}, testRunner: (context: FtrProviderContext) => diff --git a/x-pack/test/scalability/ftr_provider_context.ts b/x-pack/test/scalability/ftr_provider_context.ts index 19c510a9ec8e7..82dba4367104b 100644 --- a/x-pack/test/scalability/ftr_provider_context.ts +++ b/x-pack/test/scalability/ftr_provider_context.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; import { GenericFtrProviderContext, GenericFtrService } from '@kbn/test'; +import { services } from './services'; -export type FtrProviderContext = GenericFtrProviderContext<typeof commonFunctionalServices, {}>; +export type FtrProviderContext = GenericFtrProviderContext<typeof services, {}>; export class FtrService extends GenericFtrService<FtrProviderContext> {} diff --git a/x-pack/test/scalability/services.ts b/x-pack/test/scalability/services.ts new file mode 100644 index 0000000000000..5708fb19f7e57 --- /dev/null +++ b/x-pack/test/scalability/services.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 { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; + +const { es, esArchiver, kibanaServer, retry, supertestWithoutAuth } = commonFunctionalServices; +export const services = { + es, + esArchiver, + kibanaServer, + retry, + supertestWithoutAuth, +}; diff --git a/x-pack/test/security_api_integration/packages/helpers/saml/idp_metadata_mock_idp.xml b/x-pack/test/security_api_integration/packages/helpers/saml/idp_metadata_mock_idp.xml new file mode 100644 index 0000000000000..1049f4392d9ba --- /dev/null +++ b/x-pack/test/security_api_integration/packages/helpers/saml/idp_metadata_mock_idp.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" + entityID="urn:mock-idp"> + <md:IDPSSODescriptor WantAuthnRequestsSigned="false" + protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> + <md:KeyDescriptor use="signing"> + <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> + <ds:X509Data> + <!-- This certificate is extracted from KBN_CERT_PATH in @kbn/dev-utils and should always be in sync with it --> + <ds:X509Certificate>MIIDYjCCAkqgAwIBAgIUZ2p8K7GMXGk6xwCS9S91BUl1JnAwDQYJKoZIhvcNAQEL +BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l +cmF0ZWQgQ0EwIBcNMjMwOTIzMTUyMDE0WhgPMjA3MzA5MTAxNTIwMTRaMBExDzAN +BgNVBAMTBmtpYmFuYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOU +r52dbZ5dY0BoP2p7CEnOpG+qHTNrOAqZO/OJfniPMtpGmwAMl3WZDca6u2XkV2KE +qQyevQ2ADk6G3o8S2RU8mO/+UweuCDF7LHuSdxEGTpucidZErmVhEGUOFosL5UeB +AtIDWxvWwgK+W9Yzt5IEN2HzNCZ6h0dOSk2r9EjVMG5yF4Q6kuqOYxBT7jxoaOtO +OCrgBRummtUga4T13WZ/ZIyyHpXj2+JD4YEmrDyoTa7NLaphv0hnVhHXYoYBI/c6 +2SwwAoBlmtDmlinwSACQ3o/8eLWk0tqkIP14rc3oFh3m7D2c3c2m2HXuyoSDMfGW +beG2IE1Q3idcGmeG3qsCAwEAAaOBjDCBiTAdBgNVHQ4EFgQUMOUM7w5jmIozDvnq +RpM779m5GigwHwYDVR0jBBgwFoAUMEwqwI5b0MYpNxwaHJ9Tw1Lp3p4wPAYDVR0R +BDUwM4IUaG9zdC5kb2NrZXIuaW50ZXJuYWyCCWxvY2FsaG9zdIIEZXMwM4IEZXMw +MoIEZXMwMTAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCxqvQYXSKqgpdl +SP4gXgwipAnYsoW9qkgWQODTvSBEzUdOWme0d3j7i2l6Ur/nVSv5YjkqAv1hf/yJ +Hrk9h+j29ZO/aQ/KDh5i/gTEUnPw3Bxbw47dfn23tjMWO7NCU1fr5HNztRsa/gQr +e9s07g25u/gTfTi9Fyu0lcRe3bXOLS/mFVcuC5oxuS65R9OlbIsiORkZ2EfwuNUf +wAAYOGPIjM2VlQCvBitefsd/SzRKHdxSPy6KSjkO6MGEGo87fr7B7Nx1qp1DVrK7 +q9XeP1Cuygjg9WTcnsvWvNw8CssyuFM6X/3tGjpPasXwLvNUoG2AairK2AYTWhvS +foE31cFg</ds:X509Certificate> + </ds:X509Data> + </ds:KeyInfo> + </md:KeyDescriptor> + <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="http://localhost:5620/mock_idp/logout"/> + <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + Location="http://localhost:5620/mock_idp/logout"/> + <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + Location="http://localhost:5620/mock_idp/login"/> + <md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + Location="http://localhost:5620/mock_idp/login"/> + </md:IDPSSODescriptor> +</md:EntityDescriptor> diff --git a/x-pack/test/security_solution_api_integration/config/serverless/services_edr_workflows.ts b/x-pack/test/security_solution_api_integration/config/serverless/services_edr_workflows.ts index 299d1a509f55e..6c50ff3500050 100644 --- a/x-pack/test/security_solution_api_integration/config/serverless/services_edr_workflows.ts +++ b/x-pack/test/security_solution_api_integration/config/serverless/services_edr_workflows.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SvlUserManagerProvider } from '@kbn/test-suites-serverless/shared/services/svl_user_manager'; +import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; import { SvlCommonApiServiceProvider } from '@kbn/test-suites-serverless/shared/services/svl_common_api'; import { services as essServices } from '../ess/services_edr_workflows'; import { SecuritySolutionServerlessSuperTest } from '../services/security_solution_serverless_supertest'; @@ -15,6 +15,6 @@ export const svlServices = { ...essServices, supertest: SecuritySolutionServerlessSuperTest, securitySolutionUtils: SecuritySolutionServerlessUtils, - svlUserManager: SvlUserManagerProvider, + svlUserManager: commonFunctionalServices.samlAuth, svlCommonApi: SvlCommonApiServiceProvider, }; diff --git a/x-pack/test/security_solution_api_integration/config/services/security_solution_serverless_utils.ts b/x-pack/test/security_solution_api_integration/config/services/security_solution_serverless_utils.ts index da57ccf64860e..15b2699acbedb 100644 --- a/x-pack/test/security_solution_api_integration/config/services/security_solution_serverless_utils.ts +++ b/x-pack/test/security_solution_api_integration/config/services/security_solution_serverless_utils.ts @@ -32,6 +32,8 @@ export function SecuritySolutionServerlessUtils({ }); async function invalidateApiKey(credentials: RoleCredentials) { + // load service to call it outside mocha context + await svlUserManager.init(); await svlUserManager.invalidateM2mApiKeyWithRoleScope(credentials); } @@ -53,6 +55,8 @@ export function SecuritySolutionServerlessUtils({ const createSuperTest = async (role = 'admin') => { cleanCredentials(role); + // load service to call it outside mocha context + await svlUserManager.init(); const credentials = await svlUserManager.createM2mApiKeyWithRoleScope(role); rolesCredentials.set(role, credentials); @@ -62,6 +66,8 @@ export function SecuritySolutionServerlessUtils({ return { getUsername: async (role = 'admin') => { + // load service to call it outside mocha context + await svlUserManager.init(); const { username } = await svlUserManager.getUserData(role); return username; diff --git a/x-pack/test/security_solution_api_integration/es_archive/serverless/auditbeat/hosts/data.json.gz b/x-pack/test/security_solution_api_integration/es_archive/serverless/auditbeat/hosts/data.json.gz index 00c6963b16937..1bb35f37877b6 100644 Binary files a/x-pack/test/security_solution_api_integration/es_archive/serverless/auditbeat/hosts/data.json.gz and b/x-pack/test/security_solution_api_integration/es_archive/serverless/auditbeat/hosts/data.json.gz differ diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/rule_exceptions_execution.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/rule_exceptions_execution.ts index 126e76c8df7c1..550bb16d1dfe8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/rule_exceptions_execution.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/exceptions/workflows/basic_license_essentials_tier/rule_exceptions_execution.ts @@ -67,8 +67,7 @@ export default ({ getService }: FtrProviderContext) => { await esArchiver.unload(path); }); - // FLAKY: https://github.com/elastic/kibana/issues/181887 - describe.skip('creating rules with exceptions', () => { + describe('creating rules with exceptions', () => { beforeEach(async () => { await createAlertsIndex(supertest, log); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/custom_query.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/custom_query.ts index 2b66bc6ca49bb..0fd9938dd1999 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/custom_query.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/custom_query.ts @@ -69,6 +69,7 @@ import { deleteAllRules, deleteAllAlerts, getRuleForAlertTesting, + getLuceneRuleForTesting, } from '../../../../../../../common/utils/security_solution'; import { FtrProviderContext } from '../../../../../../ftr_provider_context'; @@ -117,7 +118,10 @@ export default ({ getService }: FtrProviderContext) => { after(async () => { await esArchiver.unload(auditbeatPath); await esArchiver.unload('x-pack/test/functional/es_archives/signals/severity_risk_overrides'); - await deleteAllAlerts(supertest, log, es, ['.preview.alerts-security.alerts-*']); + await deleteAllAlerts(supertest, log, es, [ + '.preview.alerts-security.alerts-*', + '.alerts-security.alerts-*', + ]); await deleteAllRules(supertest, log); }); @@ -2750,5 +2754,18 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('with a Lucene query rule', () => { + it('should run successfully and generate an alert that matches the lucene query', async () => { + const luceneQueryRule = getLuceneRuleForTesting(); + const { previewId } = await previewRule({ supertest, rule: luceneQueryRule }); + const previewAlerts = await getPreviewAlerts({ es, previewId }); + expect(previewAlerts.length).toBeGreaterThan(0); + expect(previewAlerts[0]?._source?.destination).toEqual( + expect.objectContaining({ domain: 'aaa.stage.11111111.hello' }) + ); + expect(previewAlerts[0]?._source?.['event.dataset']).toEqual('network_traffic.tls'); + }); + }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/eql_alert_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/eql_alert_suppression.ts index ae0682e6479ba..26764650287fc 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/eql_alert_suppression.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/eql_alert_suppression.ts @@ -64,6 +64,7 @@ export default ({ getService }: FtrProviderContext) => { log, }); + // NOTE: Add to second quality gate after feature is GA describe('@ess @serverless Alert Suppression for EQL rules', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts index 62e8fbfeffe56..30da3e2c762f4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts @@ -64,6 +64,7 @@ export default ({ getService }: FtrProviderContext) => { const getNonAggRuleQueryWithMetadata = (id: string) => `from ecs_compliant metadata _id, _index, _version ${internalIdPipe(id)}`; + // NOTE: Add to second quality gate after feature is GA describe('@ess @serverless ES|QL rule type, alert suppression', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match_alert_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match_alert_suppression.ts index 1ca09bcc8de46..0f6b0f94bdf8e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match_alert_suppression.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/indicator_match_alert_suppression.ts @@ -158,7 +158,8 @@ export default ({ getService }: FtrProviderContext) => { }, ]; - describe('@ess @serverless @serverlessQA Indicator match type rules, alert suppression', () => { + // NOTE: Add to second quality gate after feature is GA + describe('@ess @serverless Indicator match type rules, alert suppression', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning_alert_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning_alert_suppression.ts index 72818eb423b6a..7f0327c3a644e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning_alert_suppression.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/machine_learning_alert_suppression.ts @@ -88,6 +88,7 @@ export default ({ getService }: FtrProviderContext) => { 'user.name': ['root'], }; + // NOTE: Add to second quality gate after feature is GA describe('@ess @serverless Machine Learning Detection Rule - Alert Suppression', () => { describe('with an active ML Job', () => { before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/new_terms_alert_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/new_terms_alert_suppression.ts index ab741069b4ac7..285bb81c6ac93 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/new_terms_alert_suppression.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/new_terms_alert_suppression.ts @@ -56,7 +56,8 @@ export default ({ getService }: FtrProviderContext) => { const historicalWindowStart = '2019-10-13T05:00:04.000Z'; - describe('@ess @serverless @serverlessQA New terms type rules, alert suppression', () => { + // NOTE: Add to second quality gate after feature is GA + describe('@ess @serverless New terms type rules, alert suppression', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold.ts index c1681d65c05f5..0ea928d67b491 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold.ts @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { field: 'host.id', value: 1, // This value generates 7 alerts with the current esArchive }, - max_signals: 7, + max_signals: 8, }; const { logs } = await previewRule({ supertest, rule }); expect(logs[0].warnings).not.toContain(getMaxAlertsWarning()); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold_alert_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold_alert_suppression.ts index c3cf5a54b145d..c391afd0b9c12 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold_alert_suppression.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threshold_alert_suppression.ts @@ -51,7 +51,8 @@ export default ({ getService }: FtrProviderContext) => { const dataPathBuilder = new EsArchivePathBuilder(isServerless); const path = dataPathBuilder.getPath('auditbeat/hosts'); - describe('@ess @serverless @serverlessQA Threshold type rules, alert suppression', () => { + // NOTE: Add to second quality gate after feature is GA + describe('@ess @serverless Threshold type rules, alert suppression', () => { const { indexListOfDocuments, indexGeneratedDocuments } = dataGeneratorFactory({ es, index: 'ecs_compliant', diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/bootstrap_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/bootstrap_prebuilt_rules.ts new file mode 100644 index 0000000000000..97287ef0cdb93 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/bootstrap_prebuilt_rules.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + ENDPOINT_PACKAGE_NAME, + PREBUILT_RULES_PACKAGE_NAME, +} from '@kbn/security-solution-plugin/common/detection_engine/constants'; +import expect from 'expect'; +import { FtrProviderContext } from '../../../../../../ftr_provider_context'; +import { deletePrebuiltRulesFleetPackage } from '../../../../utils'; +import { deleteEndpointFleetPackage } from '../../../../utils/rules/prebuilt_rules/delete_endpoint_fleet_package'; + +export default ({ getService }: FtrProviderContext): void => { + const supertest = getService('supertest'); + const securitySolutionApi = getService('securitySolutionApi'); + + describe('@ess @serverless @skipInServerlessMKI Bootstrap Prebuilt Rules', () => { + beforeEach(async () => { + await deletePrebuiltRulesFleetPackage(supertest); + await deleteEndpointFleetPackage(supertest); + }); + + it('should install fleet packages required for detection engine to function', async () => { + const { body } = await securitySolutionApi.bootstrapPrebuiltRules().expect(200); + + expect(body).toMatchObject({ + packages: expect.arrayContaining([ + expect.objectContaining({ + name: PREBUILT_RULES_PACKAGE_NAME, + status: 'installed', + }), + expect.objectContaining({ + name: ENDPOINT_PACKAGE_NAME, + status: 'installed', + }), + ]), + }); + }); + + it('should skip installing fleet packages if they are already installed', async () => { + // Install the packages + await securitySolutionApi.bootstrapPrebuiltRules().expect(200); + // Try to install the packages again + const { body } = await securitySolutionApi.bootstrapPrebuiltRules().expect(200); + + expect(body).toMatchObject({ + packages: expect.arrayContaining([ + expect.objectContaining({ + name: PREBUILT_RULES_PACKAGE_NAME, + status: 'already_installed', + }), + expect.objectContaining({ + name: ENDPOINT_PACKAGE_NAME, + status: 'already_installed', + }), + ]), + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts index 4d17ef3f63f72..8266fba86a099 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../../../../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext): void => { describe('Rules Management - Prebuilt Rules - Prebuilt Rules Management', function () { + loadTestFile(require.resolve('./bootstrap_prebuilt_rules')); loadTestFile(require.resolve('./get_prebuilt_rules_status')); loadTestFile(require.resolve('./get_prebuilt_timelines_status')); loadTestFile(require.resolve('./install_prebuilt_rules')); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules.ts index d0f633707fa62..517ffee8ec1ff 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules.ts @@ -1088,6 +1088,15 @@ export default ({ getService }: FtrProviderContext) => { notifications_enabled: 0, notifications_disabled: 0, legacy_investigation_fields: 0, + alert_suppression: { + enabled: 0, + disabled: 0, + suppressed_per_time_period: 0, + suppressed_per_rule_execution: 0, + suppressed_fields_count: { one: 0, two: 0, three: 0 }, + suppresses_missing_fields: 0, + does_not_suppress_missing_fields: 0, + }, }); }); }); @@ -1119,6 +1128,10 @@ export default ({ getService }: FtrProviderContext) => { has_legacy_notification: false, has_notification: false, has_legacy_investigation_field: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + alert_suppression_fields_count: 0, }); }); }); @@ -1157,6 +1170,10 @@ export default ({ getService }: FtrProviderContext) => { has_notification: true, has_legacy_notification: false, has_legacy_investigation_field: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + alert_suppression_fields_count: 0, }); expect( stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled @@ -1210,6 +1227,10 @@ export default ({ getService }: FtrProviderContext) => { has_notification: true, has_legacy_notification: false, has_legacy_investigation_field: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + alert_suppression_fields_count: 0, }); expect( stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled @@ -1263,6 +1284,10 @@ export default ({ getService }: FtrProviderContext) => { has_notification: false, has_legacy_notification: true, has_legacy_investigation_field: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + alert_suppression_fields_count: 0, }); expect( stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled @@ -1316,6 +1341,10 @@ export default ({ getService }: FtrProviderContext) => { has_notification: false, has_legacy_notification: true, has_legacy_investigation_field: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + alert_suppression_fields_count: 0, }); expect( stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules_legacy_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules_legacy_action.ts index e34ee9c2005ef..ead375adb2049 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules_legacy_action.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/telemetry/trial_license_complete_tier/usage_collector/detection_rules_legacy_action.ts @@ -438,6 +438,10 @@ export default ({ getService }: FtrProviderContext) => { has_notification: false, has_legacy_notification: true, has_legacy_investigation_field: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + alert_suppression_fields_count: 0, }); expect( stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled @@ -491,6 +495,10 @@ export default ({ getService }: FtrProviderContext) => { has_notification: false, has_legacy_notification: true, has_legacy_investigation_field: false, + has_alert_suppression_per_rule_execution: false, + has_alert_suppression_per_time_period: false, + has_alert_suppression_missing_fields_strategy_do_not_suppress: false, + alert_suppression_fields_count: 0, }); expect( stats.detection_rules.detection_rule_usage.elastic_total.notifications_disabled diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_endpoint_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_endpoint_fleet_package.ts new file mode 100644 index 0000000000000..e53e24f98de3b --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_endpoint_fleet_package.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 { epmRouteService } from '@kbn/fleet-plugin/common'; +import { ENDPOINT_PACKAGE_NAME } from '@kbn/security-solution-plugin/common/detection_engine/constants'; +import type SuperTest from 'supertest'; + +/** + * Delete the endpoint package using fleet API. + * + * @param supertest Supertest instance + */ +export async function deleteEndpointFleetPackage(supertest: SuperTest.Agent) { + const resp = await supertest + .get(epmRouteService.getInfoPath(ENDPOINT_PACKAGE_NAME)) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send(); + + if (resp.status === 200 && resp.body.response.status === 'installed') { + await supertest + .delete(epmRouteService.getRemovePath(ENDPOINT_PACKAGE_NAME, resp.body.response.version)) + .set('kbn-xsrf', 'true') + .send({ force: true }); + } +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts index a3468ccb32b5a..930c9d39757f4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts @@ -6,6 +6,7 @@ */ import { epmRouteService } from '@kbn/fleet-plugin/common'; +import { PREBUILT_RULES_PACKAGE_NAME } from '@kbn/security-solution-plugin/common/detection_engine/constants'; import type SuperTest from 'supertest'; /** @@ -15,7 +16,7 @@ import type SuperTest from 'supertest'; */ export async function deletePrebuiltRulesFleetPackage(supertest: SuperTest.Agent) { const resp = await supertest - .get(epmRouteService.getInfoPath('security_detection_engine')) + .get(epmRouteService.getInfoPath(PREBUILT_RULES_PACKAGE_NAME)) .set('kbn-xsrf', 'true') .set('elastic-api-version', '2023-10-31') .send(); @@ -23,7 +24,7 @@ export async function deletePrebuiltRulesFleetPackage(supertest: SuperTest.Agent if (resp.status === 200 && resp.body.response.status === 'installed') { await supertest .delete( - epmRouteService.getRemovePath('security_detection_engine', resp.body.response.version) + epmRouteService.getRemovePath(PREBUILT_RULES_PACKAGE_NAME, resp.body.response.version) ) .set('kbn-xsrf', 'true') .send({ force: true }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts index 4ccce93c790f9..c9d97546e22c8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts @@ -18,5 +18,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./asset_criticality')); loadTestFile(require.resolve('./asset_criticality_privileges')); loadTestFile(require.resolve('./asset_criticality_csv_upload')); + loadTestFile(require.resolve('./risk_score_entity_calculation')); }); } diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts index 581af2aec6012..e35ea16c87f1a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts @@ -76,7 +76,8 @@ export default ({ getService }: FtrProviderContext): void => { }); }; - describe('@ess @serverless @serverlessQA Risk Scoring Entity Calculation API', () => { + describe('@ess @serverless @serverlessQA Risk Scoring Entity Calculation API', function () { + this.tags(['esGate']); before(async () => { enableAssetCriticalityAdvancedSetting(kibanaServer, log); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/create_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/create_exception_list_items.ts index b5a4dc3449eb1..b9a602c9fbaa3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/create_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/create_exception_list_items.ts @@ -28,7 +28,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless create_exception_list_items', () => { + describe('@ess @serverless @serverlessQA create_exception_list_items', () => { describe('validation errors', () => { it('should give a 404 error that the exception list must exist first before being able to add a list item to the exception list', async () => { const { body } = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/delete_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/delete_exception_list_items.ts index 89ff41806c97f..aa172f44bbbe6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/delete_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/delete_exception_list_items.ts @@ -28,7 +28,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless delete_exception_list_items', () => { + describe('@ess @serverless @serverlessQA delete_exception_list_items', () => { describe('delete exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/find_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/find_exception_list_items.ts index bd9068d989c19..3753c63ff7693 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/find_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/find_exception_list_items.ts @@ -30,7 +30,7 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless find_exception_list_items', () => { + describe('@ess @serverless @serverlessQA find_exception_list_items', () => { describe('find exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/read_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/read_exception_list_items.ts index d1689dcaf04b7..811796dbc5e2f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/read_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/read_exception_list_items.ts @@ -27,7 +27,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless read_exception_list_items', () => { + describe('@ess @serverless @serverlessQA read_exception_list_items', () => { describe('reading exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/update_exception_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/update_exception_list_items.ts index 46766b1ace80f..9eae3406c7aa4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/update_exception_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/items/update_exception_list_items.ts @@ -32,7 +32,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless update_exception_list_items', () => { + describe('@ess @serverless @serverlessQA update_exception_list_items', () => { describe('update exception list items', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/create_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/create_exception_lists.ts index 0a1ecfa0b0d0a..3c1c32e9046a4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/create_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/create_exception_lists.ts @@ -23,7 +23,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless create_exception_lists', () => { + describe('@ess @serverless @serverlessQA create_exception_lists', () => { describe('creating exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/delete_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/delete_exception_lists.ts index 98b387b1dc9bb..480a8acb638ae 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/delete_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/delete_exception_lists.ts @@ -24,7 +24,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless delete_exception_lists', () => { + describe('@ess @serverless @serverlessQA delete_exception_lists', () => { describe('delete exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/duplicate_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/duplicate_exception_list.ts index 927cf87b93d5a..6501ac284b7ff 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/duplicate_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/duplicate_exception_list.ts @@ -25,7 +25,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless duplicate_exception_lists', () => { + describe('@ess @serverless @serverlessQA duplicate_exception_lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/export_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/export_exception_list.ts index 726ec6269508f..f405dd38bfa96 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/export_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/export_exception_list.ts @@ -23,7 +23,7 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('@ess @serverless export_exception_list_route', () => { + describe('@ess @serverless @serverlessQA export_exception_list_route', () => { describe('exporting exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/find_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/find_exception_lists.ts index 31ece2f7a1a58..37de79ed5ee1a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/find_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/find_exception_lists.ts @@ -17,7 +17,7 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('@ess @serverless find_exception_lists', () => { + describe('@ess @serverless @serverlessQA find_exception_lists', () => { describe('find exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/get_exception_filter.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/get_exception_filter.ts index 633262ca1baa4..3c5c3ed813e65 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/get_exception_filter.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/get_exception_filter.ts @@ -30,7 +30,7 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('@ess @serverless get_exception_filter', () => { + describe('@ess @serverless @serverlessQA get_exception_filter', () => { describe('get exception filter', () => { beforeEach(async () => { await createListsIndex(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/import_exceptions.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/import_exceptions.ts index bc191618914d2..00e4da4f25dd6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/import_exceptions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/import_exceptions.ts @@ -22,7 +22,7 @@ export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); const log = getService('log'); - describe('@ess @serverless import_exceptions', () => { + describe('@ess @serverless @serverlessQA import_exceptions', () => { beforeEach(async () => { await deleteAllExceptions(supertest, log); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/read_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/read_exception_lists.ts index 89239b1a93c25..0d23afde9734b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/read_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/read_exception_lists.ts @@ -24,7 +24,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless read_exception_lists', () => { + describe('@ess @serverless @serverlessQA read_exception_lists', () => { describe('reading exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/summary_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/summary_exception_lists.ts index 2c0c9780714f7..07562ddc32d9f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/summary_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/summary_exception_lists.ts @@ -23,7 +23,7 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const log = getService('log'); - describe('@ess @serverless summary_exception_lists', () => { + describe('@ess @serverless @serverlessQA summary_exception_lists', () => { describe('summary exception lists', () => { beforeEach(async () => { await createListsIndex(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/update_exception_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/update_exception_lists.ts index f399bcde87426..b52f62a25a156 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/update_exception_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/exception_lists_items/trial_license_complete_tier/lists/update_exception_lists.ts @@ -25,7 +25,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless update_exception_lists', () => { + describe('@ess @serverless @serverlessQA update_exception_lists', () => { describe('update exception lists', () => { afterEach(async () => { await deleteAllExceptions(supertest, log); diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/create_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/create_list_items.ts index bcd38a81c564e..989e2521d3f27 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/create_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/create_list_items.ts @@ -29,7 +29,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless create_list_items', () => { + describe('@ess @serverless @serverlessQA create_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/delete_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/delete_list_items.ts index 9570646e0f846..6068845d37929 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/delete_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/delete_list_items.ts @@ -24,7 +24,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless delete_list_items', () => { + describe('@ess @serverless @serverlessQA delete_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/export_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/export_list_items.ts index 60d6a11e27d7a..1fc6116b915e6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/export_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/export_list_items.ts @@ -21,7 +21,7 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless export_list_items', () => { + describe('@ess @serverless @serverlessQA export_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/find_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/find_list_items.ts index 976b76f35e200..8779ef9915eb4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/find_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/find_list_items.ts @@ -21,7 +21,7 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless find_list_items', () => { + describe('@ess @serverless @serverlessQA find_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts index 9e4b42dce07e5..4ea906eeb2502 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/import_list_items.ts @@ -26,7 +26,7 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless import_list_items', () => { + describe('@ess @serverless @serverlessQA import_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/patch_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/patch_list_items.ts index d80c8e0951ebf..701f437518130 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/patch_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/patch_list_items.ts @@ -31,7 +31,7 @@ export default ({ getService }: FtrProviderContext) => { const retry = getService('retry'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless patch_list_items', () => { + describe('@ess @serverless @serverlessQA patch_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/read_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/read_list_items.ts index d57fb16a8625a..8864951d37501 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/read_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/read_list_items.ts @@ -25,7 +25,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless read_list_items', () => { + describe('@ess @serverless @serverlessQA read_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/update_list_items.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/update_list_items.ts index 6fa600f0cc363..dcb562938fcac 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/update_list_items.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/items/update_list_items.ts @@ -31,7 +31,7 @@ export default ({ getService }: FtrProviderContext) => { const retry = getService('retry'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless update_list_items', () => { + describe('@ess @serverless @serverlessQA update_list_items', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists.ts index aaf2be0166996..e3d6a377f35c2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists.ts @@ -27,7 +27,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless create_lists', () => { + describe('@ess @serverless @serverlessQA create_lists', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists_index.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists_index.ts index d5fcde904d2bf..634894e727619 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists_index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/create_lists_index.ts @@ -18,7 +18,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless create_list_index_route', () => { + describe('@ess @serverless @serverlessQA create_list_index_route', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/delete_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/delete_lists.ts index b7b316f2d5374..b622192754d94 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/delete_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/delete_lists.ts @@ -35,7 +35,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless delete_lists', () => { + describe('@ess @serverless @serverlessQA delete_lists', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists.ts index e9aa9645d383a..4c1bc4fc93e59 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists.ts @@ -24,7 +24,7 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless find_lists', () => { + describe('@ess @serverless @serverlessQA find_lists', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists_by_size.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists_by_size.ts index 66befac5735e7..87552111269a2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists_by_size.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/find_lists_by_size.ts @@ -27,7 +27,7 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless find_lists_by_size', () => { + describe('@ess @serverless @serverlessQA find_lists_by_size', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/patch_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/patch_lists.ts index 0f609e7938078..db6f0c73b8ba7 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/patch_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/patch_lists.ts @@ -26,7 +26,7 @@ export default ({ getService }: FtrProviderContext) => { const retry = getService('retry'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless patch_lists', () => { + describe('@ess @serverless @serverlessQA patch_lists', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/read_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/read_lists.ts index 25f8204a1fbc0..02b0056e351ca 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/read_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/read_lists.ts @@ -26,7 +26,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless read_lists', () => { + describe('@ess @serverless @serverlessQA read_lists', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/update_lists.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/update_lists.ts index f361da61e7de6..9ebc487b32855 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/update_lists.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/lists_items/trial_license_complete_tier/lists/update_lists.ts @@ -27,7 +27,7 @@ export default ({ getService }: FtrProviderContext) => { const retry = getService('retry'); const utils = getService('securitySolutionUtils'); - describe('@ess @serverless update_lists', () => { + describe('@ess @serverless @serverlessQA update_lists', () => { let supertest: TestAgent; before(async () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts index 1dad7edc63ab6..348133b1d2802 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/esql_rule.cy.ts @@ -180,6 +180,17 @@ describe( cy.get(ESQL_QUERY_BAR).contains('Error validating ES|QL'); }); + + it('shows syntax error when query is syntactically invalid - prioritizing it over missing metadata operator error', function () { + const invalidNonAggregatingQuery = 'from auditbeat* | limit 5 test'; + selectEsqlRuleType(); + fillEsqlQueryBar(invalidNonAggregatingQuery); + getDefineContinueButton().click(); + + cy.get(ESQL_QUERY_BAR).contains( + `Error validating ES|QL: "SyntaxError: extraneous input 'test' expecting <EOF>"` + ); + }); }); describe('ES|QL investigation fields', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_charts.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_charts.cy.ts index c1db8eb2b44d1..509ba40e57fc3 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_charts.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/alerts_charts.cy.ts @@ -6,13 +6,30 @@ */ import { getNewRule } from '../../../objects/rule'; -import { ALERTS_COUNT } from '../../../screens/alerts'; +import { + ALERT_SUMMARY_CHARTS, + SELECT_OVERVIEW_CHARTS, + SELECT_HISTOGRAM, + SELECT_COUNTS_TABLE, + SELECT_TREEMAP, + ALERT_SUMMARY_SEVERITY_DONUT_CHART, + ALERT_SUMMARY_RULES_TABLE, + ALERT_SUMMARY_PROGRESS_BAR_CHARTS, + ALERTS_HISTOGRAM, + ALERT_COUNT_TABLE, + ALERT_TREEMAP, + ALERTS_COUNT, + ALERT_SUMMARY_CHARTS_COLLAPSED, +} from '../../../screens/alerts'; import { clickAlertsHistogramLegend, clickAlertsHistogramLegendAddToTimeline, clickAlertsHistogramLegendFilterFor, clickAlertsHistogramLegendFilterOut, selectAlertsHistogram, + selectAlertsCountTable, + selectAlertsTreemap, + toggleKPICharts, } from '../../../tasks/alerts'; import { createRule } from '../../../tasks/api_calls/rules'; import { deleteAlertsAndRules } from '../../../tasks/api_calls/common'; @@ -25,45 +42,100 @@ import { } from '../../../screens/search_bar'; import { TOASTER } from '../../../screens/alerts_detection_rules'; -describe('Histogram legend hover actions', { tags: ['@ess', '@serverless'] }, () => { +describe('KPI visualizations in Alerts Page', { tags: ['@ess', '@serverless'] }, () => { const ruleConfigs = getNewRule(); - beforeEach(() => { deleteAlertsAndRules(); login(); createRule(getNewRule({ rule_id: 'new custom rule' })); visitWithTimeRange(ALERTS_URL); - selectAlertsHistogram(); }); - it('should should add a filter in to KQL bar', () => { - const expectedNumberOfAlerts = 1; - clickAlertsHistogramLegend(); - clickAlertsHistogramLegendFilterFor(ruleConfigs.name); - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should( - 'have.text', - `kibana.alert.rule.name: ${ruleConfigs.name}` - ); - cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alert`); - }); + context('KPI viz navigation', () => { + it('should navigate through clicking chart names', () => { + cy.log('should display summary charts by default'); + + cy.get(ALERT_SUMMARY_CHARTS).should('exist'); + cy.get(ALERT_SUMMARY_CHARTS_COLLAPSED).should('not.exist'); + cy.get(SELECT_OVERVIEW_CHARTS).should('have.class', 'euiButtonGroupButton-isSelected'); + + cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('exist'); + cy.get(ALERT_SUMMARY_RULES_TABLE).should('exist'); + cy.get(ALERT_SUMMARY_PROGRESS_BAR_CHARTS).should('exist'); + + cy.log('should display histogram charts when clicking trend button'); + + selectAlertsHistogram(); + cy.get(SELECT_HISTOGRAM).should('have.class', 'euiButtonGroupButton-isSelected'); + cy.get(ALERT_SUMMARY_CHARTS).should('not.exist'); + cy.get(ALERTS_HISTOGRAM).should('exist'); + + cy.log('should display table charts when clicking table button'); + + selectAlertsCountTable(); + cy.get(ALERT_COUNT_TABLE).should('exist'); + cy.get(SELECT_COUNTS_TABLE).should('have.class', 'euiButtonGroupButton-isSelected'); + + cy.log('should display treemap charts when clicking treemap button'); - it('should add a filter out to KQL bar', () => { - clickAlertsHistogramLegend(); - clickAlertsHistogramLegendFilterOut(ruleConfigs.name); - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should( - 'have.text', - `NOT kibana.alert.rule.name: ${ruleConfigs.name}` - ); - cy.get(ALERTS_COUNT).should('not.exist'); - - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_DELETE).click(); - cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('not.exist'); + selectAlertsTreemap(); + cy.get(ALERT_TREEMAP).should('exist'); + cy.get(SELECT_TREEMAP).should('have.class', 'euiButtonGroupButton-isSelected'); + }); + + it('should display/hide collapsed chart when clicking on the toggle', () => { + cy.log('should display summary charts by default'); + + cy.get(ALERT_SUMMARY_CHARTS_COLLAPSED).should('not.exist'); + cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('exist'); + + cy.log('should display collapsed summary when clicking toggle button'); + + toggleKPICharts(); + cy.get(ALERT_SUMMARY_CHARTS_COLLAPSED).should('exist'); + cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('not.exist'); + + cy.log('should display summary when clicking toggle button again'); + + toggleKPICharts(); + cy.get(ALERT_SUMMARY_CHARTS_COLLAPSED).should('not.exist'); + cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('exist'); + }); }); - it('should add To Timeline', () => { - clickAlertsHistogramLegend(); - clickAlertsHistogramLegendAddToTimeline(ruleConfigs.name); + context('Histogram legend hover actions', () => { + it('should should add a filter in to KQL bar', () => { + selectAlertsHistogram(); + const expectedNumberOfAlerts = 1; + clickAlertsHistogramLegend(); + clickAlertsHistogramLegendFilterFor(ruleConfigs.name); + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should( + 'have.text', + `kibana.alert.rule.name: ${ruleConfigs.name}` + ); + cy.get(ALERTS_COUNT).should('have.text', `${expectedNumberOfAlerts} alert`); + }); + + it('should add a filter out to KQL bar', () => { + selectAlertsHistogram(); + clickAlertsHistogramLegend(); + clickAlertsHistogramLegendFilterOut(ruleConfigs.name); + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should( + 'have.text', + `NOT kibana.alert.rule.name: ${ruleConfigs.name}` + ); + cy.get(ALERTS_COUNT).should('not.exist'); + + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM_DELETE).click(); + cy.get(GLOBAL_SEARCH_BAR_FILTER_ITEM).should('not.exist'); + }); + + it('should add To Timeline', () => { + selectAlertsHistogram(); + clickAlertsHistogramLegend(); + clickAlertsHistogramLegendAddToTimeline(ruleConfigs.name); - cy.get(TOASTER).should('have.text', `Added ${ruleConfigs.name} to timeline`); + cy.get(TOASTER).should('have.text', `Added ${ruleConfigs.name} to timeline`); + }); }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts index fe8a63efb1102..faf268f476652 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/changing_alert_status.cy.ts @@ -26,7 +26,7 @@ import { goToOpenedAlerts, openAlerts, openFirstAlert, - selectCountTable, + selectAlertsCountTable, waitForPageFilters, sumAlertCountFromAlertCountTable, parseAlertsCountToInt, @@ -61,7 +61,7 @@ describe.skip('Changing alert status', { tags: ['@ess', '@skipInServerless'] }, closeAlerts(); waitForAlerts(); waitForPageFilters(); - selectCountTable(); + selectAlertsCountTable(); }); it.skip('Open one alert when more than one closed alerts are selected', () => { @@ -103,7 +103,7 @@ describe.skip('Changing alert status', { tags: ['@ess', '@skipInServerless'] }, goToOpenedAlerts(); waitForAlerts(); - selectCountTable(); + selectAlertsCountTable(); cy.get(ALERTS_COUNT).should( 'have.text', @@ -126,7 +126,7 @@ describe.skip('Changing alert status', { tags: ['@ess', '@skipInServerless'] }, createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); - selectCountTable(); + selectAlertsCountTable(); }); it('Mark one alert as acknowledged when more than one open alerts are selected', () => { cy.get(ALERTS_COUNT) @@ -167,7 +167,7 @@ describe.skip('Changing alert status', { tags: ['@ess', '@skipInServerless'] }, createRule(getNewRule({ rule_id: '1', max_signals: 100 })); visit(ALERTS_URL); waitForAlertsToPopulate(); - selectCountTable(); + selectAlertsCountTable(); }); it.skip('Closes and opens alerts', () => { const numberOfAlertsToBeClosed = 3; @@ -319,7 +319,7 @@ describe.skip('Changing alert status', { tags: ['@ess', '@skipInServerless'] }, createRule(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); - selectCountTable(); + selectAlertsCountTable(); }); it('Mark one alert as acknowledged when more than one open alerts are selected', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts index aeba2d5128e2f..051f7860c8dbe 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/detection_page_filters.cy.ts @@ -29,7 +29,6 @@ import { markAcknowledgedFirstAlert, openPageFilterPopover, resetFilters, - selectCountTable, selectPageFilterValue, togglePageFilterPopover, visitAlertsPageWithCustomFilters, @@ -236,7 +235,6 @@ describe.skip(`Detections : Page Filters`, { tags: ['@ess', '@serverless'] }, () }, () => { // mark status of one alert to be acknowledged - selectCountTable(); cy.get(ALERTS_COUNT) .invoke('text') .then((noOfAlerts) => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts index fbb434c4d7263..bb4010a6db4f6 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/sourcerer/sourcerer_timeline.cy.ts @@ -31,7 +31,7 @@ import { saveSourcerer, } from '../../../tasks/sourcerer'; import { openTimelineUsingToggle } from '../../../tasks/security_main'; -import { waitForFleetSetup } from '../../../tasks/fleet_integrations'; +import { waitForRulesBootstrap } from '../../../tasks/fleet_integrations'; import { SOURCERER } from '../../../screens/sourcerer'; import { createTimeline, deleteTimelines } from '../../../tasks/api_calls/timelines'; import { getTimelineModifiedSourcerer } from '../../../objects/timeline'; @@ -42,7 +42,7 @@ const dataViews = ['logs-*', 'metrics-*', '.kibana-event-log-*']; describe('Timeline scope', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { before(() => { - waitForFleetSetup(); + waitForRulesBootstrap(); }); beforeEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts b/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts index 1f7e702873478..35c46e9f97557 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/alerts.ts @@ -72,8 +72,6 @@ export const MESSAGE = '[data-test-subj="formatted-field-message"]'; export const SELECTED_ALERTS = '[data-test-subj="selectedShowBulkActionsButton"]'; -export const SELECT_AGGREGATION_CHART = '[data-test-subj="chart-select-table"]'; - export const SEND_ALERT_TO_TIMELINE_BTN = '[data-test-subj="send-alert-to-timeline-button"]'; export const OPEN_ANALYZER_BTN = '[data-test-subj="view-in-analyzer"]'; @@ -131,6 +129,33 @@ export const ACTIONS_EXPAND_BUTTON = '[data-test-subj="euiDataGridCellExpandButt export const SHOW_TOP_N_HEADER = '[data-test-subj="topN-container"] [data-test-subj="header-section-title"]'; +export const SESSION_VIEWER_BUTTON = '[data-test-subj="session-view-button"]'; + +export const OVERLAY_CONTAINER = '[data-test-subj="overlayContainer"]'; + +/** + * Alerts KPIs + */ + +export const ALERT_CHARTS_TOGGLE_BUTTON = getDataTestSubjectSelector('query-toggle-header'); + +export const SELECT_OVERVIEW_CHARTS = getDataTestSubjectSelector('chart-select-charts'); + +export const ALERT_SUMMARY_CHARTS = getDataTestSubjectSelector('alerts-charts-panel'); + +export const ALERT_SUMMARY_SEVERITY_DONUT_CHART = + getDataTestSubjectSelector('severity-level-donut'); + +export const ALERT_SUMMARY_RULES_TABLE = getDataTestSubjectSelector('alerts-by-rule-panel'); + +export const ALERT_SUMMARY_PROGRESS_BAR_CHARTS = getDataTestSubjectSelector( + 'alerts-progress-bar-panel' +); + +export const ALERT_SUMMARY_CHARTS_COLLAPSED = getDataTestSubjectSelector('chart-collapse'); + +export const ALERTS_HISTOGRAM = getDataTestSubjectSelector('alerts-histogram-panel'); + export const ALERTS_HISTOGRAM_LEGEND = '[data-test-subj="alerts-histogram-panel"] .echLegendItem__action'; @@ -146,12 +171,13 @@ export const LEGEND_ACTIONS = { COPY: (ruleName: string) => `[data-test-subj="legend-${ruleName}-embeddable_copyToClipboard"]`, }; -export const SESSION_VIEWER_BUTTON = '[data-test-subj="session-view-button"]'; +export const ALERT_COUNT_TABLE = getDataTestSubjectSelector('alertsCountPanel'); -export const OVERLAY_CONTAINER = '[data-test-subj="overlayContainer"]'; +export const SELECT_COUNTS_TABLE = '[data-test-subj="chart-select-table"]'; -export const ALERT_SUMMARY_SEVERITY_DONUT_CHART = - getDataTestSubjectSelector('severity-level-donut'); +export const SELECT_TREEMAP = getDataTestSubjectSelector('chart-select-treemap'); + +export const ALERT_TREEMAP = getDataTestSubjectSelector('treemapPanel'); export const ALERT_TAGGING_CONTEXT_MENU_ITEM = '[data-test-subj="alert-tags-context-menu-item"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/support/saml_auth.ts b/x-pack/test/security_solution_cypress/cypress/support/saml_auth.ts index b6701a8b4b553..1b5c2adcae455 100644 --- a/x-pack/test/security_solution_cypress/cypress/support/saml_auth.ts +++ b/x-pack/test/security_solution_cypress/cypress/support/saml_auth.ts @@ -9,6 +9,8 @@ import { ToolingLog } from '@kbn/tooling-log'; import { SecurityRoleName } from '@kbn/security-solution-plugin/common/test'; import { HostOptions, SamlSessionManager } from '@kbn/test'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { resolve } from 'path'; import { DEFAULT_SERVERLESS_ROLE } from '../env_var_names_constants'; export const samlAuthentication = async ( @@ -31,30 +33,27 @@ export const samlAuthentication = async ( // If config.env.PROXY_ORG is set, it means that proxy service is used to create projects. Define the proxy org filename to override the roles. const rolesFilename = config.env.PROXY_ORG ? `${config.env.PROXY_ORG}.json` : undefined; + const cloudUsersFilePath = resolve(REPO_ROOT, '.ftr', rolesFilename ?? 'role_users.json'); on('task', { getSessionCookie: async (role: string | SecurityRoleName): Promise<string> => { - const sessionManager = new SamlSessionManager( - { - hostOptions, - log, - isCloud: config.env.CLOUD_SERVERLESS, - }, - rolesFilename - ); + const sessionManager = new SamlSessionManager({ + hostOptions, + log, + isCloud: config.env.CLOUD_SERVERLESS, + cloudUsersFilePath, + }); return sessionManager.getInteractiveUserSessionCookieWithRoleScope(role); }, getFullname: async ( role: string | SecurityRoleName = DEFAULT_SERVERLESS_ROLE ): Promise<string> => { - const sessionManager = new SamlSessionManager( - { - hostOptions, - log, - isCloud: config.env.CLOUD_SERVERLESS, - }, - rolesFilename - ); + const sessionManager = new SamlSessionManager({ + hostOptions, + log, + isCloud: config.env.CLOUD_SERVERLESS, + cloudUsersFilePath, + }); const { full_name: fullName } = await sessionManager.getUserData(role); return fullName; }, diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/alerts.ts b/x-pack/test/security_solution_cypress/cypress/tasks/alerts.ts index abd405e1d6c73..6adcd97bd8c28 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/alerts.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/alerts.ts @@ -18,7 +18,9 @@ import { MARK_ALERT_ACKNOWLEDGED_BTN, OPEN_ALERT_BTN, SEND_ALERT_TO_TIMELINE_BTN, - SELECT_AGGREGATION_CHART, + ALERT_CHARTS_TOGGLE_BUTTON, + SELECT_COUNTS_TABLE, + SELECT_TREEMAP, TAKE_ACTION_POPOVER_BTN, TIMELINE_CONTEXT_MENU_BTN, CLOSE_FLYOUT, @@ -264,14 +266,22 @@ export const openAlerts = () => { cy.get(OPEN_ALERT_BTN).click(); }; -export const selectCountTable = () => { - cy.get(SELECT_AGGREGATION_CHART).click({ force: true }); +export const toggleKPICharts = () => { + cy.get(ALERT_CHARTS_TOGGLE_BUTTON).click(); +}; + +export const selectAlertsCountTable = () => { + cy.get(SELECT_COUNTS_TABLE).click(); }; export const selectAlertsHistogram = () => { cy.get(SELECT_HISTOGRAM).click(); }; +export const selectAlertsTreemap = () => { + cy.get(SELECT_TREEMAP).click(); +}; + export const goToAcknowledgedAlerts = () => { /* * below line commented because alertPageFiltersEnabled feature flag diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts index 273491ebd2efe..f4273cad22299 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/prebuilt_rules.ts @@ -8,6 +8,7 @@ import { PerformRuleInstallationResponseBody, PERFORM_RULE_INSTALLATION_URL, + BOOTSTRAP_PREBUILT_RULES_URL, } from '@kbn/security-solution-plugin/common/api/detection_engine'; import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common/detection_engine/constants'; import type { PrePackagedRulesStatusResponse } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; @@ -203,8 +204,7 @@ export const getRuleAssets = (index: string | undefined = '.kibana_security_solu /* during e2e tests, and allow for manual installation of mock rules instead. */ export const preventPrebuiltRulesPackageInstallation = () => { cy.log('Prevent prebuilt rules package installation'); - cy.intercept('POST', '/api/fleet/epm/packages/_bulk*', {}); - cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*', {}); + cy.intercept('POST', BOOTSTRAP_PREBUILT_RULES_URL, {}); }; /** diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/fleet_integrations.ts b/x-pack/test/security_solution_cypress/cypress/tasks/fleet_integrations.ts index 7105ebc4df70f..f0b59a39b8478 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/fleet_integrations.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/fleet_integrations.ts @@ -6,6 +6,7 @@ */ import { + BOOTSTRAP_PREBUILT_RULES_URL, GET_ALL_INTEGRATIONS_URL, Integration, } from '@kbn/security-solution-plugin/common/api/detection_engine'; @@ -21,10 +22,10 @@ export const mockFleetIntegrations = (integrations: Integration[] = []) => { }).as('integrations'); }; -export const waitForFleetSetup = () => { - cy.intercept('POST', '/api/fleet/epm/packages/_bulk?prerelease=true').as('fleetSetup'); +export const waitForRulesBootstrap = () => { + cy.intercept('POST', BOOTSTRAP_PREBUILT_RULES_URL).as('rulesBootstrap'); cy.clearLocalStorage(); login(); visitGetStartedPage(); - cy.wait('@fleetSetup'); + cy.wait('@rulesBootstrap'); }; diff --git a/x-pack/test/security_solution_cypress/cypress/tsconfig.json b/x-pack/test/security_solution_cypress/cypress/tsconfig.json index d07d03536f7f4..b7223115bbcb6 100644 --- a/x-pack/test/security_solution_cypress/cypress/tsconfig.json +++ b/x-pack/test/security_solution_cypress/cypress/tsconfig.json @@ -42,5 +42,6 @@ "@kbn/actions-plugin", "@kbn/alerts-ui-shared", "@kbn/securitysolution-endpoint-exceptions-common", + "@kbn/repo-info", ] } diff --git a/x-pack/test/security_solution_endpoint/services/index.ts b/x-pack/test/security_solution_endpoint/services/index.ts index c9b9d59fa56f6..666378e4492c5 100644 --- a/x-pack/test/security_solution_endpoint/services/index.ts +++ b/x-pack/test/security_solution_endpoint/services/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SvlUserManagerProvider } from '@kbn/test-suites-serverless/shared/services/svl_user_manager'; +import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; import { SvlCommonApiServiceProvider } from '@kbn/test-suites-serverless/shared/services/svl_common_api'; import { services as xPackFunctionalServices } from '../../functional/services'; import { IngestManagerProvider } from '../../common/services/ingest_manager'; @@ -43,7 +43,7 @@ export const svlServices = { supertestWithoutAuth: KibanaSupertestWithCertWithoutAuthProvider, svlCommonApi: SvlCommonApiServiceProvider, - svlUserManager: SvlUserManagerProvider, + svlUserManager: commonFunctionalServices.samlAuth, }; export type Services = typeof services | typeof svlServices; diff --git a/x-pack/test/security_solution_endpoint/tsconfig.json b/x-pack/test/security_solution_endpoint/tsconfig.json index 6b35306a0d693..e4ce04de12a59 100644 --- a/x-pack/test/security_solution_endpoint/tsconfig.json +++ b/x-pack/test/security_solution_endpoint/tsconfig.json @@ -27,5 +27,6 @@ "@kbn/ftr-common-functional-ui-services", "@kbn/test", "@kbn/test-subj-selector", + "@kbn/ftr-common-functional-services", ] } diff --git a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts index 066a004df3814..aa4f68e1fedd8 100644 --- a/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts +++ b/x-pack/test/task_manager_claimer_mget/test_suites/task_manager/health_route.ts @@ -140,7 +140,12 @@ export default function ({ getService }: FtrProviderContext) { }, }, request_capacity: 1000, - max_workers: 10, + capacity: { + config: 10, + as_workers: 10, + as_cost: 20, + }, + claim_strategy: 'unsafe_mget', }); }); diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 3449df8d6f23c..974a206d71e3f 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -176,6 +176,7 @@ "@kbn/osquery-plugin", "@kbn/entities-schema", "@kbn/actions-simulators-plugin", - "@kbn/cases-api-integration-test-plugin" + "@kbn/cases-api-integration-test-plugin", + "@kbn/mock-idp-utils" ] } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/console/autocomplete_entities.ts b/x-pack/test_serverless/api_integration/test_suites/common/console/autocomplete_entities.ts index 64adeea57a9be..72640103c0ef9 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/console/autocomplete_entities.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/console/autocomplete_entities.ts @@ -7,8 +7,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { RoleCredentials } from '../../../../shared/services'; -import { InternalRequestHeader } from '../../../../shared/services/svl_common_api'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default ({ getService }: FtrProviderContext) => { const svlCommonApi = getService('svlCommonApi'); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/response_headers.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/response_headers.ts index 797ad2ef8e911..562e98d33866a 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/response_headers.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/response_headers.ts @@ -17,13 +17,10 @@ export default function ({ getService }: FtrProviderContext) { let roleAuthc: RoleCredentials; describe('security/response_headers', function () { - // fails on MKI, see https://github.com/elastic/kibana/issues/188714 - this.tags(['failsOnMKI']); - const baseCSP = `script-src 'report-sample' 'self'; worker-src 'report-sample' 'self' blob:; style-src 'report-sample' 'self' 'unsafe-inline'; frame-ancestors 'self'`; const defaultCOOP = 'same-origin'; const defaultPermissionsPolicy = - 'camera=(), display-capture=(), fullscreen=(self), geolocation=(), microphone=(), web-share=()'; + 'camera=(), display-capture=(), fullscreen=(self), geolocation=(), microphone=(), web-share=();report-to=violations-endpoint'; const defaultStrictTransportSecurity = 'max-age=31536000; includeSubDomains'; const defaultReferrerPolicy = 'strict-origin-when-cross-origin'; const defaultXContentTypeOptions = 'nosniff'; diff --git a/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts b/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts index 2bf5eda659da4..6518dcd30f147 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/telemetry/telemetry_config.ts @@ -68,6 +68,21 @@ export default function telemetryConfigTest({ getService }: FtrProviderContext) journeyName: 'my-ftr-test', }, }); + + // Sends "null" to remove the label + await supertestWithoutAuth + .put('/internal/core/_settings') + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .set('elastic-api-version', '1') + .send({ 'telemetry.labels.journeyName': null }) + .expect(200, { ok: true }); + + await supertestWithoutAuth + .get('/api/telemetry/v2/config') + .set(svlCommonApi.getCommonRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200, initialConfig); }); }); } diff --git a/x-pack/test_serverless/functional/page_objects/svl_management_page.ts b/x-pack/test_serverless/functional/page_objects/svl_management_page.ts index fe78ec9bc07d9..e77e77c4fa76c 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_management_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_management_page.ts @@ -48,5 +48,16 @@ export function SvlManagementPageProvider({ getService }: FtrProviderContext) { async clickIngestPipelinesManagementCard() { await testSubjects.click('app-card-ingest_pipelines'); }, + + // Spaces card + async assertSpacesManagementCardExists() { + await testSubjects.existOrFail('app-card-spaces'); + }, + async assertSpacesManagementCardDoesNotExist() { + await testSubjects.missingOrFail('app-card-spaces'); + }, + async clickSpacesManagementCard() { + await testSubjects.click('app-card-spaces'); + }, }; } diff --git a/x-pack/test_serverless/functional/test_suites/common/console/console.ts b/x-pack/test_serverless/functional/test_suites/common/console/console.ts index bd5d7b224fba3..b7eec68c637af 100644 --- a/x-pack/test_serverless/functional/test_suites/common/console/console.ts +++ b/x-pack/test_serverless/functional/test_suites/common/console/console.ts @@ -6,34 +6,9 @@ */ import expect from '@kbn/expect'; +import { DEFAULT_INPUT_VALUE } from '@kbn/console-plugin/common/constants'; import { FtrProviderContext } from '../../../ftr_provider_context'; -const DEFAULT_REQUEST = ` -# Welcome to the Dev Tools Console! -# -# You can use Console to explore the Elasticsearch API. See the \n Elasticsearch API reference to learn more: -# https://www.elastic.co/guide/en/elasticsearch/reference/current/rest\n -apis.html -# -# Here are a few examples to get you started. - - -# Create an index -PUT /my-index - - -# Add a document to my-index -POST /my-index/_doc -{ - "id": "park_rocky-mountain", - "title": "Rocky Mountain", - "description": "Bisected north to south by the Continental Divide, \n this portion of the Rockies has ecosystems varying from over 150 \n riparian lakes to montane and subalpine forests to treeless \n alpine tundra." -} - - -# Perform a search in my-index -GET /my-index/_search?q="rocky mountain" -`.trim(); - export default function ({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const log = getService('log'); @@ -56,18 +31,18 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should show the default request', async () => { await retry.try(async () => { - const actualRequest = await PageObjects.console.getRequest(); + const actualRequest = await PageObjects.console.monaco.getEditorText(); log.debug(actualRequest); - expect(actualRequest.trim()).to.eql(DEFAULT_REQUEST); + expect(actualRequest.replace(/\s/g, '')).to.eql(DEFAULT_INPUT_VALUE.replace(/\s/g, '')); }); }); it('default request response should include `"timed_out" : false`', async () => { const expectedResponseContains = `"timed_out": false`; - await PageObjects.console.selectAllRequests(); + await PageObjects.console.monaco.selectAllRequests(); await PageObjects.console.clickPlay(); await retry.try(async () => { - const actualResponse = await PageObjects.console.getResponse(); + const actualResponse = await PageObjects.console.monaco.getOutputText(); log.debug(actualResponse); expect(actualResponse).to.contain(expectedResponseContains); }); diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts index 2808686ab0b09..c99f556b2f52d 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_default_app_state.ts @@ -127,7 +127,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('data view mode', () => { + // FLAKY: https://github.com/elastic/kibana/issues/189994 + describe.skip('data view mode', () => { it('should render default columns and row height', async () => { await PageObjects.common.navigateToActualUrl('discover', undefined, { ensureCurrentUrl: false, diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_row_additional_leading_controls.ts b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_row_additional_leading_controls.ts new file mode 100644 index 0000000000000..c91dae10bc4ea --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/extensions/_get_row_additional_leading_controls.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import kbnRison from '@kbn/rison'; +import type { FtrProviderContext } from '../../../../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'discover', 'svlCommonPage']); + const testSubjects = getService('testSubjects'); + const dataViews = getService('dataViews'); + + describe('extension getRowAdditionalLeadingControls', () => { + before(async () => { + await PageObjects.svlCommonPage.loginAsAdmin(); + }); + describe('ES|QL mode', () => { + it('should render logs controls for logs data source', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-logs | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + + it('should not render logs controls for non-logs data source', async () => { + const state = kbnRison.encode({ + dataSource: { type: 'esql' }, + query: { esql: 'from my-example-metrics | sort @timestamp desc' }, + }); + await PageObjects.common.navigateToApp('discover', { + hash: `/?_a=${state}`, + }); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + }); + + describe('data view mode', () => { + it('should render logs controls for logs data source', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-logs'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + + it('should not render logs controls for non-logs data source', async () => { + await PageObjects.common.navigateToApp('discover'); + await dataViews.switchTo('my-example-metrics'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked'); + await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl'); + }); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/index.ts b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/index.ts index e8c8f1234aab5..d0e23c825870b 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/index.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/context_awareness/index.ts @@ -38,6 +38,7 @@ export default function ({ getService, getPageObjects, loadTestFile }: FtrProvid loadTestFile(require.resolve('./_root_profile')); loadTestFile(require.resolve('./_data_source_profile')); loadTestFile(require.resolve('./extensions/_get_row_indicator_provider')); + loadTestFile(require.resolve('./extensions/_get_row_additional_leading_controls')); loadTestFile(require.resolve('./extensions/_get_doc_viewer')); loadTestFile(require.resolve('./extensions/_get_cell_renderers')); loadTestFile(require.resolve('./extensions/_get_default_app_state')); diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts index 80fff5ef76014..0d1e2b3de6a02 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/esql/_esql_view.ts @@ -198,8 +198,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const cell = await dataGrid.getCellElementExcludingControlColumns(0, 1); expect(await cell.getVisibleText()).to.be(' - '); expect(await dataGrid.getHeaders()).to.eql([ - 'Control column', 'Select column', + 'Control column', 'Numberbytes', 'machine.ram_range', ]); diff --git a/x-pack/test_serverless/functional/test_suites/common/discover/group1/_discover.ts b/x-pack/test_serverless/functional/test_suites/common/discover/group1/_discover.ts index a5274115cfbaf..531202481ba84 100644 --- a/x-pack/test_serverless/functional/test_suites/common/discover/group1/_discover.ts +++ b/x-pack/test_serverless/functional/test_suites/common/discover/group1/_discover.ts @@ -221,8 +221,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.uiSettings.update({ 'dateFormat:tz': 'America/Phoenix' }); await PageObjects.common.navigateToApp('discover'); await PageObjects.header.awaitKibanaChrome(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); await PageObjects.timePicker.setDefaultAbsoluteRange(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); await queryBar.clearQuery(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.discover.waitUntilSearchingHasFinished(); log.debug( 'check that the newest doc timestamp is now -7 hours from the UTC time in the first test' diff --git a/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts b/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts index a9d88803d0b4b..723acdaf9c3ed 100644 --- a/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts +++ b/x-pack/test_serverless/functional/test_suites/common/platform_security/navigation/management_nav_cards.ts @@ -5,6 +5,9 @@ * 2.0. */ +// Note: this suite is currently only called from the feature flags test config: +// x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts + import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../ftr_provider_context'; @@ -59,6 +62,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // `--xpack.cloud.organization_url: '/account/members'`, expect(url).to.contain('/account/members'); }); + + it('displays the spaces management card, and will navigate to the spaces management UI', async () => { + await pageObjects.svlManagementPage.assertSpacesManagementCardExists(); + await pageObjects.svlManagementPage.clickSpacesManagementCard(); + + const url = await browser.getCurrentUrl(); + expect(url).to.contain('/management/kibana/spaces'); + }); }); describe('as viewer', function () { @@ -96,6 +107,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(url).to.contain('/account/members'); }); + it('should not display the spaces management card', async () => { + await retry.waitFor('page to be visible', async () => { + return await testSubjects.exists('cards-navigation-page'); + }); + await pageObjects.svlManagementPage.assertSpacesManagementCardDoesNotExist(); + }); + describe('API keys management card - search solution', function () { this.tags(['skipSvlOblt', 'skipSvlSec']); diff --git a/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/export_transform.ts b/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/export_transform.ts index 80de679ca7b49..fa7de0335ff8d 100644 --- a/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/export_transform.ts +++ b/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/export_transform.ts @@ -42,7 +42,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('allows to mutate the objects during an export', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -50,24 +50,23 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { type: ['test-export-transform'], excludeExportDetails: true, }) - .expect(200) - .then((resp) => { - const objects = parseNdJson(resp.text); - expect(objects.map((obj) => ({ id: obj.id, enabled: obj.attributes.enabled }))).to.eql([ - { - id: 'type_1-obj_1', - enabled: false, - }, - { - id: 'type_1-obj_2', - enabled: false, - }, - ]); - }); + .expect(200); + + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => ({ id: obj.id, enabled: obj.attributes.enabled }))).to.eql([ + { + id: 'type_1-obj_1', + enabled: false, + }, + { + id: 'type_1-obj_2', + enabled: false, + }, + ]); }); it('allows to add additional objects to an export', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -80,15 +79,13 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ], excludeExportDetails: true, }) - .expect(200) - .then((resp) => { - const objects = parseNdJson(resp.text); - expect(objects.map((obj) => obj.id)).to.eql(['type_2-obj_1', 'type_dep-obj_1']); - }); + .expect(200); + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql(['type_2-obj_1', 'type_dep-obj_1']); }); it('allows to add additional objects to an export when exporting by type', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -96,20 +93,18 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { type: ['test-export-add'], excludeExportDetails: true, }) - .expect(200) - .then((resp) => { - const objects = parseNdJson(resp.text); - expect(objects.map((obj) => obj.id)).to.eql([ - 'type_2-obj_1', - 'type_2-obj_2', - 'type_dep-obj_1', - 'type_dep-obj_2', - ]); - }); + .expect(200); + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql([ + 'type_2-obj_1', + 'type_2-obj_2', + 'type_dep-obj_1', + 'type_dep-obj_2', + ]); }); it('returns a 400 when the type causes a transform error', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -117,21 +112,19 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { type: ['test-export-transform-error'], excludeExportDetails: true, }) - .expect(400) - .then((resp) => { - const { attributes, ...error } = resp.body; - expect(error).to.eql({ - error: 'Bad Request', - message: 'Error transforming objects to export', - statusCode: 400, - }); - expect(attributes.cause).to.eql('Error during transform'); - expect(attributes.objects.map((obj: any) => obj.id)).to.eql(['type_4-obj_1']); - }); + .expect(400); + const { attributes, ...error } = resp.body; + expect(error).to.eql({ + error: 'Bad Request', + message: 'Error transforming objects to export', + statusCode: 400, + }); + expect(attributes.cause).to.eql('Error during transform'); + expect(attributes.objects.map((obj: any) => obj.id)).to.eql(['type_4-obj_1']); }); it('returns a 400 when the type causes an invalid transform', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -139,17 +132,15 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { type: ['test-export-invalid-transform'], excludeExportDetails: true, }) - .expect(400) - .then((resp) => { - expect(resp.body).to.eql({ - error: 'Bad Request', - message: 'Invalid transform performed on objects to export', - statusCode: 400, - attributes: { - objectKeys: ['test-export-invalid-transform|type_3-obj_1'], - }, - }); - }); + .expect(400); + expect(resp.body).to.eql({ + error: 'Bad Request', + message: 'Invalid transform performed on objects to export', + statusCode: 400, + attributes: { + objectKeys: ['test-export-invalid-transform|type_3-obj_1'], + }, + }); }); }); @@ -172,7 +163,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('execute export transforms for reference objects', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -186,21 +177,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { includeReferencesDeep: true, excludeExportDetails: true, }) - .expect(200) - .then((resp) => { - const objects = parseNdJson(resp.text).sort((obj1, obj2) => - obj1.id.localeCompare(obj2.id) - ); - expect(objects.map((obj) => obj.id)).to.eql([ - 'type_1-obj_1', - 'type_1-obj_2', - 'type_2-obj_1', - 'type_dep-obj_1', - ]); + .expect(200); + const objects = parseNdJson(resp.text).sort((obj1, obj2) => obj1.id.localeCompare(obj2.id)); + expect(objects.map((obj) => obj.id)).to.eql([ + 'type_1-obj_1', + 'type_1-obj_2', + 'type_2-obj_1', + 'type_dep-obj_1', + ]); - expect(objects[0].attributes.enabled).to.eql(false); - expect(objects[1].attributes.enabled).to.eql(false); - }); + expect(objects[0].attributes.enabled).to.eql(false); + expect(objects[1].attributes.enabled).to.eql(false); }); }); @@ -223,7 +210,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('should only export objects returning `true` for `isExportable`', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -237,21 +224,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { includeReferencesDeep: true, excludeExportDetails: true, }) - .expect(200) - .then((resp) => { - const objects = parseNdJson(resp.text).sort((obj1, obj2) => - obj1.id.localeCompare(obj2.id) - ); - expect(objects.map((obj) => `${obj.type}:${obj.id}`)).to.eql([ - 'test-is-exportable:1', - 'test-is-exportable:3', - 'test-is-exportable:5', - ]); - }); + .expect(200); + const objects = parseNdJson(resp.text).sort((obj1, obj2) => obj1.id.localeCompare(obj2.id)); + expect(objects.map((obj) => `${obj.type}:${obj.id}`)).to.eql([ + 'test-is-exportable:1', + 'test-is-exportable:3', + 'test-is-exportable:5', + ]); }); it('lists objects that got filtered', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -265,31 +248,29 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { includeReferencesDeep: true, excludeExportDetails: false, }) - .expect(200) - .then((resp) => { - const objects = parseNdJson(resp.text); - const exportDetails = objects[ - objects.length - 1 - ] as unknown as SavedObjectsExportResultDetails; + .expect(200); + const objects = parseNdJson(resp.text); + const exportDetails = objects[ + objects.length - 1 + ] as unknown as SavedObjectsExportResultDetails; - expect(exportDetails.excludedObjectsCount).to.eql(2); - expect(exportDetails.excludedObjects).to.eql([ - { - type: 'test-is-exportable', - id: '2', - reason: 'excluded', - }, - { - type: 'test-is-exportable', - id: '4', - reason: 'excluded', - }, - ]); - }); + expect(exportDetails.excludedObjectsCount).to.eql(2); + expect(exportDetails.excludedObjects).to.eql([ + { + type: 'test-is-exportable', + id: '2', + reason: 'excluded', + }, + { + type: 'test-is-exportable', + id: '4', + reason: 'excluded', + }, + ]); }); it('excludes objects if `isExportable` throws', async () => { - await supertest + const resp = await supertest .post('/api/saved_objects/_export') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) @@ -307,24 +288,20 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { includeReferencesDeep: true, excludeExportDetails: false, }) - .expect(200) - .then((resp) => { - const objects = parseNdJson(resp.text); - expect(objects.length).to.eql(2); - expect([objects[0]].map((obj) => `${obj.type}:${obj.id}`)).to.eql([ - 'test-is-exportable:5', - ]); - const exportDetails = objects[ - objects.length - 1 - ] as unknown as SavedObjectsExportResultDetails; - expect(exportDetails.excludedObjects).to.eql([ - { - type: 'test-is-exportable', - id: 'error', - reason: 'predicate_error', - }, - ]); - }); + .expect(200); + const objects = parseNdJson(resp.text); + expect(objects.length).to.eql(2); + expect([objects[0]].map((obj) => `${obj.type}:${obj.id}`)).to.eql(['test-is-exportable:5']); + const exportDetails = objects[ + objects.length - 1 + ] as unknown as SavedObjectsExportResultDetails; + expect(exportDetails.excludedObjects).to.eql([ + { + type: 'test-is-exportable', + id: 'error', + reason: 'predicate_error', + }, + ]); }); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/find.ts b/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/find.ts index 986479518bfd4..c792d0f7fdbc1 100644 --- a/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/find.ts +++ b/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/find.ts @@ -44,37 +44,35 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await kibanaServer.savedObjects.cleanStandardList(); }); - it('returns saved objects with importableAndExportable types', async () => - await supertest + it('returns saved objects with importableAndExportable types', async () => { + const resp = await supertest .get('/api/kibana/management/saved_objects/_find?type=test-hidden-importable-exportable') .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) - .expect(200) - .then((resp) => { - expect( - resp.body.saved_objects.map((so: { id: string; type: string }) => ({ - id: so.id, - type: so.type, - })) - ).to.eql([ - { - type: 'test-hidden-importable-exportable', - id: 'ff3733a0-9fty-11e7-ahb3-3dcb94193fab', - }, - ]); - })); + .expect(200); + expect( + resp.body.saved_objects.map((so: { id: string; type: string }) => ({ + id: so.id, + type: so.type, + })) + ).to.eql([ + { + type: 'test-hidden-importable-exportable', + id: 'ff3733a0-9fty-11e7-ahb3-3dcb94193fab', + }, + ]); + }); - it('returns empty response for non importableAndExportable types', async () => - await supertest + it('returns empty response for non importableAndExportable types', async () => { + const resp = await supertest .get( '/api/kibana/management/saved_objects/_find?type=test-hidden-non-importable-exportable' ) .set(svlCommonApi.getCommonRequestHeader()) .set(svlCommonApi.getInternalRequestHeader()) - .expect(200) - .then((resp) => { - expect(resp.body.saved_objects).to.eql([]); - })); + .expect(200); + expect(resp.body.saved_objects).to.eql([]); + }); }); }); } diff --git a/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/hidden_from_http_apis.ts b/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/hidden_from_http_apis.ts index d5edfb0e73bf2..6857dd73a2c4f 100644 --- a/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/hidden_from_http_apis.ts +++ b/x-pack/test_serverless/functional/test_suites/common/saved_objects_management/hidden_from_http_apis.ts @@ -11,6 +11,11 @@ import type { Response } from 'supertest'; import { SavedObject } from '@kbn/core/types'; import { FtrProviderContext } from '../../../ftr_provider_context'; +interface MinimalSO { + id: string; + type: string; +} + function parseNdJson(input: string): Array<SavedObject<any>> { return input.split('\n').map((str) => JSON.parse(str)); } @@ -118,10 +123,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { .expect(200) .then((resp) => { expect( - resp.body.saved_objects.map((so: { id: string; type: string }) => ({ - id: so.id, - type: so.type, - })) + resp.body.saved_objects + .map((so: MinimalSO) => ({ + id: so.id, + type: so.type, + })) + .sort((a: MinimalSO, b: MinimalSO) => (a.id > b.id ? 1 : -1)) ).to.eql([ { id: 'hidden-from-http-apis-1', diff --git a/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection_enabled.ts b/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection_enabled.ts new file mode 100644 index 0000000000000..e998b25d24a11 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/common/spaces/spaces_selection_enabled.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// Note: this suite is currently only called from the feature flags test config: +// x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts +// This file can take the place of the spaces_selection test file when spaces +// are enabled permanently in serverless. + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getPageObject, getService }: FtrProviderContext) { + const svlCommonPage = getPageObject('svlCommonPage'); + const svlCommonNavigation = getService('svlCommonNavigation'); + const testSubjects = getService('testSubjects'); + + describe('space selection', function () { + describe('as Viewer', function () { + before(async () => { + await svlCommonPage.loginAsViewer(); + }); + + it('displays the space selection menu in header', async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + + await testSubjects.existOrFail('spacesNavSelector'); + }); + + it(`does not display the manage button in the space selection menu`, async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + await testSubjects.click('spacesNavSelector'); + await testSubjects.missingOrFail('manageSpaces'); + }); + }); + + describe('as Admin', function () { + before(async () => { + await svlCommonPage.loginAsAdmin(); + }); + + it('displays the space selection menu in header', async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + await testSubjects.existOrFail('spacesNavSelector'); + }); + + it(`displays the manage button in the space selection menu`, async () => { + await svlCommonNavigation.navigateToKibanaHome(); + await svlCommonPage.assertProjectHeaderExists(); + await testSubjects.click('spacesNavSelector'); + await testSubjects.existOrFail('manageSpaces'); + }); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts index ffbc3365ba930..f28a8bdb40377 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/config.feature_flags.ts @@ -24,6 +24,7 @@ export default createTestConfig({ '--xpack.security.roleManagementEnabled=true', `--xpack.cloud.base_url='https://cloud.elastic.co'`, `--xpack.cloud.organization_url='/account/members'`, + `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities ], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], diff --git a/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts index ef9523962e4af..24a7c41caeaee 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/index.feature_flags.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./infra')); loadTestFile(require.resolve('../common/platform_security/navigation/management_nav_cards.ts')); loadTestFile(require.resolve('../common/platform_security/roles.ts')); + loadTestFile(require.resolve('../common/spaces/spaces_selection_enabled.ts')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts index 8e7294d35f1b8..6fcfea151db12 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts @@ -86,7 +86,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('render content virtual column properly', async () => { it('should render log level and log message when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); expect(cellValue.includes('A sample log')).to.be(true); @@ -95,7 +95,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render log message when present and skip log level when missing', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(1, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(1, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(false); expect(cellValue.includes('A sample log')).to.be(true); @@ -104,7 +104,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render message from error object when top level message not present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(2, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(2, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); expect(cellValue.includes('error.message')).to.be(true); @@ -114,7 +114,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render message from event.original when top level message and error.message not present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(3, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(3, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); expect(cellValue.includes('event.original')).to.be(true); @@ -124,7 +124,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the whole JSON when neither message, error.message and event.original are present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(4, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(4, 2); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('info')).to.be(true); @@ -138,7 +138,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('on cell expansion with no message field should open JSON Viewer', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - await dataGrid.clickCellExpandButton(4, 4); + await dataGrid.clickCellExpandButtonExcludingControlColumns(4, 2); await testSubjects.existOrFail('dataTableExpandCellActionJsonPopover'); }); }); @@ -146,7 +146,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('on cell expansion with message field should open regular popover', async () => { await navigateToLogsExplorer(); await retry.tryForTime(TEST_TIMEOUT, async () => { - await dataGrid.clickCellExpandButton(3, 4); + await dataGrid.clickCellExpandButtonExcludingControlColumns(3, 2); await testSubjects.existOrFail('euiDataGridExpansionPopover'); }); }); @@ -155,7 +155,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('render resource virtual column properly', async () => { it('should render service name and host name when present', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 3); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); const cellValue = await cellElement.getVisibleText(); expect(cellValue.includes('synth-service')).to.be(true); expect(cellValue.includes('synth-host')).to.be(true); @@ -169,7 +169,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should render a popover with cell actions when a chip on content column is clicked', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const logLevelChip = await cellElement.findByTestSubject('*logLevelBadge-'); await logLevelChip.click(); // Check Filter In button is present @@ -183,7 +183,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the table filtered where log.level value is info when filter in action is clicked', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const logLevelChip = await cellElement.findByTestSubject('*logLevelBadge-'); const actionSelector = 'dataTableCellAction_addToFilterAction_log.level'; @@ -204,7 +204,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the table filtered where log.level value is not info when filter out action is clicked', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 4); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 2); const logLevelChip = await cellElement.findByTestSubject('*logLevelBadge-'); const actionSelector = 'dataTableCellAction_removeFromFilterAction_log.level'; @@ -223,7 +223,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the table filtered where service.name value is selected', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 3); + const cellElement = await dataGrid.getCellElementExcludingControlColumns(0, 1); const serviceNameChip = await cellElement.findByTestSubject( 'dataTablePopoverChip_service.name' ); diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/custom_control_columns.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/custom_control_columns.ts index fea974fd0096e..c1f4692f19fdf 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/custom_control_columns.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/custom_control_columns.ts @@ -48,7 +48,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render control column with proper header', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { // First control column has no title, so empty string, leading control column has title - expect(await dataGrid.getControlColumnHeaderFields()).to.eql(['', 'actions']); + expect(await dataGrid.getControlColumnHeaderFields()).to.eql(['', '', '', '']); }); }); @@ -60,27 +60,27 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should render the malformed icon in the leading control column if malformed doc exists', async () => { + it('should render the degraded icon in the leading control column if degraded doc exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(1, 1); - const malformedButton = await cellElement.findByTestSubject('docTableDegradedDocExist'); - expect(malformedButton).to.not.be.empty(); + const cellElement = await dataGrid.getCellElement(1, 2); + const degradedButton = await cellElement.findByTestSubject('docTableDegradedDocExist'); + expect(degradedButton).to.not.be.empty(); }); }); - it('should render the disabled malformed icon in the leading control column when malformed doc does not exists', async () => { + it('should render the disabled degraded icon in the leading control column when degraded doc does not exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(0, 1); - const malformedDisableButton = await cellElement.findByTestSubject( + const cellElement = await dataGrid.getCellElement(0, 2); + const degradedDisableButton = await cellElement.findByTestSubject( 'docTableDegradedDocDoesNotExist' ); - expect(malformedDisableButton).to.not.be.empty(); + expect(degradedDisableButton).to.not.be.empty(); }); }); it('should render the stacktrace icon in the leading control column when stacktrace exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(4, 1); + const cellElement = await dataGrid.getCellElement(4, 3); const stacktraceButton = await cellElement.findByTestSubject('docTableStacktraceExist'); expect(stacktraceButton).to.not.be.empty(); }); @@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should render the stacktrace icon disabled in the leading control column when stacktrace does not exists', async () => { await retry.tryForTime(TEST_TIMEOUT, async () => { - const cellElement = await dataGrid.getCellElement(1, 1); + const cellElement = await dataGrid.getCellElement(1, 3); const stacktraceButton = await cellElement.findByTestSubject( 'docTableStacktraceDoesNotExist' ); @@ -116,10 +116,7 @@ function generateLogsData({ to, count = 1 }: { to: string; count?: number }) { }) ); - const malformedDocs = timerange( - moment(to).subtract(2, 'second'), - moment(to).subtract(1, 'second') - ) + const degradedDocs = timerange(moment(to).subtract(2, 'second'), moment(to).subtract(1, 'second')) .interval('1m') .rate(1) .generator((timestamp) => @@ -128,7 +125,7 @@ function generateLogsData({ to, count = 1 }: { to: string; count?: number }) { .map(() => { return log .create() - .message('A malformed doc') + .message('A degraded doc') .logLevel(MORE_THAN_1024_CHARS) .timestamp(timestamp) .defaults({ 'service.name': 'synth-service' }); @@ -186,5 +183,5 @@ function generateLogsData({ to, count = 1 }: { to: string; count?: number }) { }) ); - return [logs, malformedDocs, logsWithErrorMessage, logsWithErrorException, logsWithErrorInLog]; + return [logs, degradedDocs, logsWithErrorMessage, logsWithErrorException, logsWithErrorInLog]; } diff --git a/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts index c4b29fdfb443d..ee652f0f7eb2d 100644 --- a/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/search/config.feature_flags.ts @@ -24,6 +24,7 @@ export default createTestConfig({ `--xpack.cloud.base_url='https://cloud.elastic.co'`, `--xpack.cloud.organization_url='/account/members'`, `--xpack.security.roleManagementEnabled=true`, + `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities ], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], diff --git a/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts index 2126b0c7d9839..acf3e6f98b7db 100644 --- a/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/search/index.feature_flags.ts @@ -12,5 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { // add tests that require feature flags, defined in config.feature_flags.ts loadTestFile(require.resolve('../common/platform_security/navigation/management_nav_cards.ts')); loadTestFile(require.resolve('../common/platform_security/roles.ts')); + loadTestFile(require.resolve('../common/spaces/spaces_selection_enabled.ts')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts index beddcaddf7208..84e80f154bee9 100644 --- a/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/security/config.feature_flags.ts @@ -22,6 +22,7 @@ export default createTestConfig({ `--xpack.security.roleManagementEnabled=true`, `--xpack.cloud.base_url='https://cloud.elastic.co'`, `--xpack.cloud.organization_url='/account/members'`, + `--xpack.spaces.maxSpaces=100`, // enables spaces UI capabilities ], // load tests in the index file testFiles: [require.resolve('./index.feature_flags.ts')], diff --git a/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts b/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts index 7a0c4fbe8bd0e..a260942f1a4fc 100644 --- a/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts +++ b/x-pack/test_serverless/functional/test_suites/security/index.feature_flags.ts @@ -12,5 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { // add tests that require feature flags, defined in config.feature_flags.ts loadTestFile(require.resolve('../common/platform_security/navigation/management_nav_cards.ts')); loadTestFile(require.resolve('../common/platform_security/roles.ts')); + loadTestFile(require.resolve('../common/spaces/spaces_selection_enabled.ts')); }); } diff --git a/x-pack/test_serverless/shared/config.base.ts b/x-pack/test_serverless/shared/config.base.ts index 6d9e134baef6f..099bc6455db3d 100644 --- a/x-pack/test_serverless/shared/config.base.ts +++ b/x-pack/test_serverless/shared/config.base.ts @@ -139,6 +139,9 @@ export default async () => { })}`, '--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', `--server.publicBaseUrl=${servers.kibana.protocol}://${servers.kibana.hostname}:${servers.kibana.port}`, + // configure security reponse header report-to settings to mimic MKI configuration + `--csp.report_to=${JSON.stringify(['violations-endpoint'])}`, + `--permissionsPolicy.report_to=${JSON.stringify(['violations-endpoint'])}`, ], }, diff --git a/x-pack/test_serverless/shared/services/index.ts b/x-pack/test_serverless/shared/services/index.ts index 71c048cdf772b..631d047d8fcfb 100644 --- a/x-pack/test_serverless/shared/services/index.ts +++ b/x-pack/test_serverless/shared/services/index.ts @@ -9,12 +9,13 @@ import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; import { SupertestProvider } from './supertest'; import { SvlCommonApiServiceProvider } from './svl_common_api'; import { SvlReportingServiceProvider } from './svl_reporting'; -import { SvlUserManagerProvider } from './svl_user_manager'; import { DataViewApiProvider } from './data_view_api'; -export type { RoleCredentials } from './svl_user_manager'; -export type { InternalRequestHeader } from './svl_common_api'; -export type { SupertestWithoutAuthProviderType } from '@kbn/ftr-common-functional-services'; +export type { + InternalRequestHeader, + RoleCredentials, + SupertestWithoutAuthProviderType, +} from '@kbn/ftr-common-functional-services'; const SupertestWithoutAuthProvider = commonFunctionalServices.supertestWithoutAuth; @@ -23,6 +24,6 @@ export const services = { supertestWithoutAuth: SupertestWithoutAuthProvider, svlCommonApi: SvlCommonApiServiceProvider, svlReportingApi: SvlReportingServiceProvider, - svlUserManager: SvlUserManagerProvider, + svlUserManager: commonFunctionalServices.samlAuth, dataViewApi: DataViewApiProvider, }; diff --git a/x-pack/test_serverless/shared/services/svl_common_api.ts b/x-pack/test_serverless/shared/services/svl_common_api.ts index 99ffe486dd4d7..f207c0ed3bb0e 100644 --- a/x-pack/test_serverless/shared/services/svl_common_api.ts +++ b/x-pack/test_serverless/shared/services/svl_common_api.ts @@ -22,10 +22,11 @@ export type InternalRequestHeader = typeof INTERNAL_REQUEST_HEADERS; export function SvlCommonApiServiceProvider({}: FtrProviderContext) { return { + // call it from 'samlAuth' service when tests are migrated to deployment-agnostic getCommonRequestHeader() { return COMMON_REQUEST_HEADERS; }, - + // call it from 'samlAuth' service when tests are migrated to deployment-agnostic getInternalRequestHeader(): InternalRequestHeader { return INTERNAL_REQUEST_HEADERS; }, diff --git a/x-pack/test_serverless/shared/services/svl_reporting.ts b/x-pack/test_serverless/shared/services/svl_reporting.ts index cd231ee8e77e1..9d3d7941ec503 100644 --- a/x-pack/test_serverless/shared/services/svl_reporting.ts +++ b/x-pack/test_serverless/shared/services/svl_reporting.ts @@ -11,8 +11,8 @@ import type { ReportingJobResponse } from '@kbn/reporting-plugin/server/types'; import { REPORTING_DATA_STREAM_WILDCARD_WITH_LEGACY } from '@kbn/reporting-server'; import rison from '@kbn/rison'; import { FtrProviderContext } from '../../functional/ftr_provider_context'; -import { RoleCredentials } from './svl_user_manager'; -import { InternalRequestHeader } from './svl_common_api'; +import { RoleCredentials } from '.'; +import { InternalRequestHeader } from '.'; const API_HEADER: [string, string] = ['kbn-xsrf', 'reporting']; diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index 3ad0d679d126a..b1f73748b5e0d 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -100,5 +100,6 @@ "@kbn/observability-ai-assistant-plugin", "@kbn/ml-trained-models-utils", "@kbn/test-suites-src", + "@kbn/console-plugin", ] } diff --git a/yarn.lock b/yarn.lock index 6de55f6d100cd..959a7b4ad5db2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3468,10 +3468,6 @@ version "0.0.0" uid "" -"@kbn/assets-data-access-plugin@link:x-pack/plugins/observability_solution/assets_data_access": - version "0.0.0" - uid "" - "@kbn/audit-log-plugin@link:x-pack/test/security_api_integration/plugins/audit_log": version "0.0.0" uid "" @@ -4836,6 +4832,10 @@ version "0.0.0" uid "" +"@kbn/entities-data-access-plugin@link:x-pack/plugins/observability_solution/entities_data_access": + version "0.0.0" + uid "" + "@kbn/entities-schema@link:x-pack/packages/kbn-entities-schema": version "0.0.0" uid "" @@ -7204,10 +7204,10 @@ "@launchdarkly/js-sdk-common" "2.5.0" semver "7.5.4" -"@launchdarkly/node-server-sdk@^9.4.7": - version "9.4.7" - resolved "https://registry.yarnpkg.com/@launchdarkly/node-server-sdk/-/node-server-sdk-9.4.7.tgz#d8f923943b1750efa4c0f80baad079103ac5f700" - integrity sha512-9blKdS4c/ZGR0XjB8EqifDTdk08WBX/KZ91lZIc3ND3WA9D3qagysynaVkpBo3FiymHyCYyvwq4ZjiSMOIkrPw== +"@launchdarkly/node-server-sdk@^9.5.0": + version "9.5.0" + resolved "https://registry.yarnpkg.com/@launchdarkly/node-server-sdk/-/node-server-sdk-9.5.0.tgz#171c235a16836611e3c6d8d95945afa24832fc10" + integrity sha512-nNH011mToDKEUz5Nrux3bdatgmHj+SQGUCCD1+R5wgzpdvwZxhi7w1W+9Wl6QrqB+LcpBfKp2+vMQH/4KLsDXg== dependencies: "@launchdarkly/js-server-sdk-common" "2.4.4" https-proxy-agent "^5.0.1" @@ -8230,12 +8230,12 @@ require-from-string "^2.0.2" uri-js "^4.2.2" -"@redocly/cli@^1.18.1": - version "1.18.1" - resolved "https://registry.yarnpkg.com/@redocly/cli/-/cli-1.18.1.tgz#a811d33be83885cdae973bcdf30455b8bb2bffc1" - integrity sha512-+bRKj46R9wvTzMdnoYfMueJ9/ek0NprEsQNowV7XcHgOXifeFFikRtBFcpkwqCNxaQ/nWAJn4LHZaFcssbcHow== +"@redocly/cli@^1.19.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@redocly/cli/-/cli-1.19.0.tgz#fa7ac88c50b8850c1358bb68a22a3330efe0e0b0" + integrity sha512-ev6J0eD+quprvW9PVCl9JmRFZbj6cuK+mnYPAjcrPvesy2RF752fflcpgQjGnyFaGb1Cj+DiwDi3dYr3EAp04A== dependencies: - "@redocly/openapi-core" "1.18.1" + "@redocly/openapi-core" "1.19.0" abort-controller "^3.0.0" chokidar "^3.5.1" colorette "^1.2.0" @@ -8260,10 +8260,10 @@ resolved "https://registry.yarnpkg.com/@redocly/config/-/config-0.7.0.tgz#e8d06dc1f2d9cb9a4b5c5ce09afbf8536b32161c" integrity sha512-6GKxTo/9df0654Mtivvr4lQnMOp+pRj9neVywmI5+BwfZLTtkJnj2qB3D6d8FHTr4apsNOf6zTa5FojX0Evh4g== -"@redocly/openapi-core@1.18.1", "@redocly/openapi-core@^1.4.0": - version "1.18.1" - resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.18.1.tgz#b23cdc647db3ecb787995e9f78b569c13c33fb0d" - integrity sha512-y2ZR3aaVF80XRVoFP0Dp2z5DeCOilPTuS7V4HnHIYZdBTfsqzjkO169h5JqAaifnaLsLBhe3YArdgLb7W7wW6Q== +"@redocly/openapi-core@1.19.0", "@redocly/openapi-core@^1.4.0": + version "1.19.0" + resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.19.0.tgz#8c6db2f0286b7776d79e392335f89f702ea19432" + integrity sha512-ezK6qr80sXvjDgHNrk/zmRs9vwpIAeHa0T/qmo96S+ib4ThQ5a8f3qjwEqxMeVxkxCTbkaY9sYSJKOxv4ejg5w== dependencies: "@redocly/ajv" "^8.11.0" "@redocly/config" "^0.7.0" @@ -26464,10 +26464,10 @@ react-colorful@^5.1.2: resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.5.1.tgz#29d9c4e496f2ca784dd2bb5053a3a4340cfaf784" integrity sha512-M1TJH2X3RXEt12sWkpa6hLc/bbYS0H6F4rIqjQZ+RxNBstpY67d9TrFXtqdZwhpmBXcCwEi7stKqFue3ZRkiOg== -react-diff-view@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/react-diff-view/-/react-diff-view-3.2.0.tgz#8fbf04782d78423903a59202ce7533f6312c1cc3" - integrity sha512-p58XoqMxgt71ujpiDQTs9Za3nqTawt1E4bTzKsYSqr8I8br6cjQj1b66HxGnV8Yrc6MD6iQPqS1aZiFoGqEw+g== +react-diff-view@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/react-diff-view/-/react-diff-view-3.2.1.tgz#cc1473955fae999c1d486638c4425ceb48f3d465" + integrity sha512-JoDahgiyeReeH9W9lrI3Z4c4esbd/HNAOdThj6Pce/ZAukFBmXSbZ4Qv8ayo7yow+fTpRNfqtQ9gX5nArEi08w== dependencies: classnames "^2.3.2" diff-match-patch "^1.0.5"